Python & TypeScript · one contract, many providers

Python & TypeScript · um contrato, muitos cofres

Secret vault,
quick and easy.

Ler segredos,
rápido e fácil.

Your code asks for database/password. Whether that lives in AWS Secrets Manager, GCP Parameter Manager or a dictionary in a test does not change the call — only which distribution you install.

Seu código pede database/password. Se isso mora no AWS Secrets Manager, no GCP Parameter Manager ou num dicionário de teste, a chamada não muda — muda apenas qual distribuição você instala.

How a name is built
Como um nome é montado
envprod
projectbilling
prefixnot setvazio
namedatabase/password
resolves to resolve para /prod/billing/database/password
pythontypescript
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_aws_parameter_store import (
    AwsParameterStoreProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-parameter-store",
        env="prod",
        project="billing",
        options=AwsParameterStoreProviderOptions(
            region="us-east-1",
        ),
    )
)

secret = await client.get_secret("database/password")
# secret.value — reads /prod/billing/database/password
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.AwsParameterStore,
    env: 'prod',
    project: 'billing',
    options: { region: 'us-east-1' },
});

const secret = await client.getSecret('database/password');
// secret.value — reads /prod/billing/database/password
01

Why use Secret Kernel

Por que usar o Secret Kernel

One contract in front of five providers, and what that buys you.

Um contrato na frente de cinco cofres (no código, o provider), e o que você ganha com isso.

  • Ask by name, get the value. One call returns the decrypted secret. There is no provider SDK to learn.
  • Every provider behaves the same. The naming rules, the per-call options and the errors do not change with the provider: a missing secret is SecretNotFoundError, code scrt_not_found, on AWS, on GCP and in memory. All five pass the same contract test suite.
  • Change providers, not code. Moving from AWS to GCP changes the provider, its options and the installed package. Every get_secretgetSecret call stays as written.
  • Tests without the cloud. The in-memory provider follows the same contract, so tests and CI make the same calls the service makes, with no network and no credentials.
  • Python and TypeScript, one contract. Each follows its own convention — get_secret, getSecret — and both share the behavior, the defaults and the error codes.
  • Peça pelo nome, receba o valor. Uma chamada devolve o segredo já descriptografado. Não há SDK de cofre para aprender.
  • Todo cofre se comporta igual. As regras de nome, as opções por chamada e os erros não mudam com o cofre: um segredo ausente é SecretNotFoundError, código scrt_not_found, na AWS, no GCP e em memória. Os cinco passam pela mesma suíte de testes de contrato.
  • Troque o cofre, não o código. Sair da AWS para o GCP muda o cofre, suas opções e o pacote instalado. Toda chamada a get_secretgetSecret continua como foi escrita.
  • Testes sem nuvem. O cofre in-memory segue o mesmo contrato, então testes e CI fazem as mesmas chamadas que o serviço, sem rede e sem credencial.
  • Python e TypeScript, um contrato. Cada um segue a convenção da sua linguagem — get_secret, getSecret — e os dois compartilham comportamento, defaults e códigos de erro.
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_aws_parameter_store import (
    AwsParameterStoreProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-parameter-store",
        env="prod",
        project="billing",
        options=AwsParameterStoreProviderOptions(
            region="us-east-1",
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_gcp_parameter_manager import (
    GcpParameterManagerProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="gcp-parameter-manager",
        env="prod",
        project="billing",
        options=GcpParameterManagerProviderOptions(
            project_id="my-gcp-project",
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_in_memory import (
    InMemoryProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="in-memory",
        env="prod",
        project="billing",
        options=InMemoryProviderOptions(
            env_secrets=True,
        ),
    )
)


def load_env() -> None:
    # .env
    # SECRET_KERNEL_PROD_BILLING_DATABASE_PASSWORD=s3cr3t
    # SECRET_KERNEL_PROD_BILLING_API_TOKEN=local-token
    from dotenv import load_dotenv
    load_dotenv()


try:
    load_env()
    secret = await client.get_secret("database/password")
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.AwsParameterStore,
    env: 'prod',
    project: 'billing',
    options: { region: 'us-east-1' },
});

try {
    const secret = await client.getSecret('database/password');
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.GcpParameterManager,
    env: 'prod',
    project: 'billing',
    options: { projectId: 'my-gcp-project' },
});

try {
    const secret = await client.getSecret('database/password');
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.InMemory,
    env: 'prod',
    project: 'billing',
    options: { envSecrets: true },
});

function loadEnv() {
    // .env
    // SECRET_KERNEL_PROD_BILLING_DATABASE_PASSWORD=s3cr3t
    // SECRET_KERNEL_PROD_BILLING_API_TOKEN=local-token
    process.loadEnvFile();
}

try {
    loadEnv();
    const secret = await client.getSecret('database/password');
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}
02

Install and first read

Instalação e primeira leitura

Nothing is bundled. Install the contract, the shared core, the factory and the one provider you need.

Nada vem embutido. Instale o contrato, o core compartilhado, a factory e o pacote do único cofre de que você precisa.

Step 1 · install the base

Passo 1 · instale a base

The contract, the shared core and the factory. No provider yet — that is step 2, and it is a separate distribution so an AWS-only service never pulls a GCP SDK.

O contrato, o core compartilhado e a factory. Nenhum cofre ainda — isso é o passo 2, e ele é uma distribuição separada, então um serviço só-AWS nunca baixa um SDK do GCP.

contracts depends on nothing. A consumer that only declares types or catches errors installs that one package alone.

contracts não depende de nada. Quem só declara tipos ou captura erros instala apenas esse pacote.

uvnpmshell
$ uv add secret-kernel-contracts secret-kernel-core \
    secret-kernel-factory
$ npm install @secret-kernel/contracts @secret-kernel/core \
    @secret-kernel/factory

Step 2 · add a provider and read

Passo 2 · adicione um cofre e leia

Pick where the secret lives. The call is identical on all five — only the provider alias and its typed options change, which is the whole point of the contract.

Escolha onde o segredo mora. A chamada é idêntica nos cinco — mudam apenas o alias do cofre e suas opções tipadas, que é justamente o propósito do contrato.

For local development, CI and tests. Reads preloaded values or environment variables, so nothing needs a cloud account.

Para desenvolvimento local, CI e testes. Lê valores pré-carregados ou variáveis de ambiente, então nada precisa de conta em nuvem.

With env_secretsenvSecrets, the key is the resolved name without the leading slash, non-alphanumerics replaced by _, uppercased and prefixed: /dev/my-app/database becomes SECRET_KERNEL_DEV_MY_APP_DATABASE. Preloaded secrets win over the environment.

Com env_secretsenvSecrets, a chave é o nome resolvido sem a barra inicial, não-alfanuméricos trocados por _, em maiúsculas e com prefixo: /dev/my-app/database vira SECRET_KERNEL_DEV_MY_APP_DATABASE. Segredos pré-carregados vencem o ambiente.

uvnpmshell
$ uv add secret-kernel-provider-in-memory
$ npm install @secret-kernel/provider-in-memory
in-memory
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_in_memory import (
    InMemoryProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="in-memory",
        env="prod",
        project="billing",
        options=InMemoryProviderOptions(
            env_secrets=True,
        ),
    )
)


def load_env() -> None:
    # .env
    # SECRET_KERNEL_PROD_BILLING_DATABASE_PASSWORD=s3cr3t
    # SECRET_KERNEL_PROD_BILLING_API_TOKEN=local-token
    from dotenv import load_dotenv
    load_dotenv()


try:
    load_env()
    secret = await client.get_secret("database/password")
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.InMemory,
    env: 'prod',
    project: 'billing',
    options: { envSecrets: true },
});

function loadEnv() {
    // .env
    // SECRET_KERNEL_PROD_BILLING_DATABASE_PASSWORD=s3cr3t
    // SECRET_KERNEL_PROD_BILLING_API_TOKEN=local-token
    process.loadEnvFile();
}

try {
    loadEnv();
    const secret = await client.getSecret('database/password');
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}

For low-cost secrets in AWS. Reads SecureString parameters.

Para segredos de baixo custo na AWS. Lê parâmetros SecureString.

Pass credentials to be explicit, or leave it out and the AWS SDK's own chain applies. Explicit credentials take precedence.

Passe credentials para ser explícito, ou omita e a própria cadeia do SDK da AWS se aplica. Credenciais explícitas têm precedência.

uvnpmshell
$ uv add secret-kernel-provider-aws-parameter-store
$ npm install @secret-kernel/provider-aws-parameter-store
aws-parameter-store
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_aws_parameter_store import (
    AwsCredentials,
    AwsParameterStoreProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-parameter-store",
        env="prod",
        project="billing",
        options=AwsParameterStoreProviderOptions(
            region="us-east-1",
            credentials=AwsCredentials(
                access_key_id="AKIAIOSFODNN7EXAMPLE",
                secret_access_key=(
                    "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
                ),
            ),
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
    # reads /prod/billing/database/password
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.AwsParameterStore,
    env: 'prod',
    project: 'billing',
    options: {
        region: 'us-east-1',
        credentials: {
            accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
            secretAccessKey:
                'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
        },
    },
});

try {
    const secret = await client.getSecret('database/password');
    // reads /prod/billing/database/password
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}

For AWS secrets that need rotation and a dedicated lifecycle. Reads the AWSCURRENT stage.

Para segredos da AWS que precisam de rotação e ciclo de vida próprio. Lê o estágio AWSCURRENT.

Same options as aws-parameter-store. A binary-only secret raises SecretCapabilityNotSupportedError: binary payloads are outside the base contract.

Mesmas opções do aws-parameter-store. Um segredo apenas binário levanta SecretCapabilityNotSupportedError: payloads binários ficam fora do contrato base.

uvnpmshell
$ uv add secret-kernel-provider-aws-secrets-manager
$ npm install @secret-kernel/provider-aws-secrets-manager
aws-secrets-manager
from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_aws_secrets_manager import (
    AwsCredentials,
    AwsSecretsManagerProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-secrets-manager",
        env="prod",
        project="billing",
        options=AwsSecretsManagerProviderOptions(
            region="us-east-1",
            credentials=AwsCredentials(
                access_key_id="AKIAIOSFODNN7EXAMPLE",
                secret_access_key=(
                    "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
                ),
            ),
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
    # reads /prod/billing/database/password, stage AWSCURRENT
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.AwsSecretsManager,
    env: 'prod',
    project: 'billing',
    options: {
        region: 'us-east-1',
        credentials: {
            accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
            secretAccessKey:
                'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
        },
    },
});

try {
    const secret = await client.getSecret('database/password');
    // reads /prod/billing/database/password, stage AWSCURRENT
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}

For low-cost secrets in GCP. Reads the rendered payload of the latest version.

Para segredos de baixo custo no GCP. Lê o payload renderizado da última versão.

project_idprojectId has no default; location defaults to global. GCP does not accept / in a name, so the resolved name is joined with _.

project_idprojectId não tem padrão; location tem padrão global. O GCP não aceita / num nome, então o nome resolvido é unido com _.

credentials is the service account key file, as Google ships it. Leave it out and the Google SDK's own defaults apply.

credentials é o arquivo de chave da service account, como o Google o entrega. Omita e os próprios padrões do SDK do Google se aplicam.

uvnpmshell
$ uv add secret-kernel-provider-gcp-parameter-manager
$ npm install @secret-kernel/provider-gcp-parameter-manager
gcp-parameter-manager
import json
from pathlib import Path

from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_gcp_parameter_manager import (
    GcpParameterManagerProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="gcp-parameter-manager",
        env="prod",
        project="billing",
        options=GcpParameterManagerProviderOptions(
            project_id="my-gcp-project",
            credentials=json.loads(
                Path("service-account.json").read_text()
            ),
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
    # reads projects/my-gcp-project/locations/global/
    #   parameters/prod_billing_database_password/
    #   versions/latest
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { readFileSync } from 'node:fs';
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.GcpParameterManager,
    env: 'prod',
    project: 'billing',
    options: {
        projectId: 'my-gcp-project',
        credentials: JSON.parse(
            readFileSync('service-account.json', 'utf8'),
        ),
    },
});

try {
    const secret = await client.getSecret('database/password');
    // reads projects/my-gcp-project/locations/global/
    //   parameters/prod_billing_database_password/
    //   versions/latest
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}

For GCP secrets that need rotation and a dedicated lifecycle. Reads the latest version.

Para segredos do GCP que precisam de rotação e ciclo de vida próprio. Lê a última versão.

project_idprojectId has no default. GCP does not accept / in a name, so the resolved name is joined with _.

project_idprojectId não tem padrão. O GCP não aceita / num nome, então o nome resolvido é unido com _.

credentials is the service account key file, as Google ships it. Leave it out and the Google SDK's own defaults apply.

credentials é o arquivo de chave da service account, como o Google o entrega. Omita e os próprios padrões do SDK do Google se aplicam.

uvnpmshell
$ uv add secret-kernel-provider-gcp-secret-manager
$ npm install @secret-kernel/provider-gcp-secret-manager
gcp-secret-manager
import json
from pathlib import Path

from secret_kernel.contracts import SecretNotFoundError
from secret_kernel.factory import (
    CreateSecretClientConfig,
    create_secret_client,
)
from secret_kernel.provider_gcp_secret_manager import (
    GcpSecretManagerProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="gcp-secret-manager",
        env="prod",
        project="billing",
        options=GcpSecretManagerProviderOptions(
            project_id="my-gcp-project",
            credentials=json.loads(
                Path("service-account.json").read_text()
            ),
        ),
    )
)

try:
    secret = await client.get_secret("database/password")
    # reads projects/my-gcp-project/secrets/
    #   prod_billing_database_password/versions/latest
except SecretNotFoundError as error:
    error.code  # "scrt_not_found", on any provider
import { readFileSync } from 'node:fs';
import { SecretNotFoundError } from '@secret-kernel/contracts';
import {
    SecretProvider,
    createSecretClient,
} from '@secret-kernel/factory';

const client = await createSecretClient({
    provider: SecretProvider.GcpSecretManager,
    env: 'prod',
    project: 'billing',
    options: {
        projectId: 'my-gcp-project',
        credentials: JSON.parse(
            readFileSync('service-account.json', 'utf8'),
        ),
    },
});

try {
    const secret = await client.getSecret('database/password');
    // reads projects/my-gcp-project/secrets/
    //   prod_billing_database_password/versions/latest
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        error.code; // 'scrt_not_found', on any provider
    }
}
03

How a name is built

Como um nome é montado

A secret name is joined from what the client knows and what the call asks for.

O nome do segredo é montado a partir do que o client sabe e do que a chamada pede.

the client and the callo client e a chamada
client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-parameter-store",
        env="prod",
        project="billing",
    )
)

secret = await client.get_secret("database/password")
# reads /prod/billing/database/password
const client = await createSecretClient({
    provider: SecretProvider.AwsParameterStore,
    env: 'prod',
    project: 'billing',
});

const secret = await client.getSecret('database/password');
// reads /prod/billing/database/password
env / project / prefix / name
/prod  /billing  /—       /database/password
/prod/billing/database/password

All three leading parts are optional: a client with none of them reads /database/password.

As três primeiras partes são opcionais: um client sem nenhuma delas lê /database/password.

PartParte OptionalOpcional Accepts a slashAceita barra RuleRegra
env YesSim NoNão The deployment context — prod, staging. A single segment. A slash here would silently add a level, and the caller would only find out through a secret that cannot be found. O contexto de deploy — prod, staging. Um único segmento. Uma barra aqui adicionaria um nível silenciosamente, e quem chamou só descobriria por um segredo que não é encontrado.
project YesSim NoNão Groups a set of secrets, usually one service or application. Same rule as env. Agrupa um conjunto de segredos, normalmente um serviço ou aplicação. Mesma regra do env.
prefix YesSim YesSim Adds any further levels you want. A string or a list of segments. A per-call prefix replaces the client's rather than adding to it. Adiciona quantos níveis você quiser. Uma string ou uma lista de segmentos. Um prefix por chamada substitui o do client em vez de somar a ele.
namename NoNão YesSim What the call asks for. Accepts /, because adding levels is exactly what it is for. O que a chamada pede. Aceita /, porque adicionar níveis é exatamente para isso que serve.

Always use / as the separator

Use sempre / como separador

When you want separators in a name or a prefix — they are not required — use /, whatever the provider. One convention in your code, and nothing to rename when the provider changes. Each provider then converts the resolved name into the form its service accepts.

Quando quiser usar separadores no nome ou no prefix — eles não são obrigatórios —, use /, seja qual for o cofre. Assim o seu código escreve os nomes de um jeito só, e nada precisa ser renomeado quando você troca de cofre. Cada cofre então converte o nome resolvido para o formato que o seu serviço aceita.

GCP does not accept / in a secret or parameter ID, so both GCP providers drop the leading slash and join the levels with _. AWS takes the name as it is.

O GCP não aceita / no ID de um segredo ou parâmetro, então os dois cofres do GCP removem a barra inicial e juntam os níveis com _. A AWS recebe o nome como ele é.

one name, as each provider reads itum nome, como cada cofre lê
your code              seu código             /prod/billing/database/password
aws-secrets-manager    /prod/billing/database/password
aws-parameter-store    /prod/billing/database/password
gcp-secret-manager     prod_billing_database_password
gcp-parameter-manager  prod_billing_database_password

The separator a provider uses in its service is a provider option, physical_name_separatorphysicalNameSeparator — GCP also accepts -, AWS also _ and -. A naming convention that is not the kernel's goes through resolve_nameresolveName on the GCP providers. See Providers.

O separador que cada cofre usa é uma opção dele, physical_name_separatorphysicalNameSeparator — o GCP também aceita -, a AWS também _ e -. Uma convenção de nomes que não é a do kernel passa por resolve_nameresolveName nos cofres do GCP. Veja Cofres.

04

Changing the name for one call

Mudando o nome para uma chamada

Each of the three client-level parts can be dropped, and each can be replaced.

Cada uma das três partes do nível do client pode ser derrubada, e cada uma pode ser substituída.

Omitting always wins over replacing. Passing both omit_projectomitProject and project drops the segment.

Omitir sempre vence substituir. Passar omit_projectomitProject e project juntos derruba o segmento.

Replacing env is never implicit: the client's value applies unless a call says otherwise, in that call alone. It exists for the caller who knows better than the process context — a local run reading the one secret that lives elsewhere.

Substituir o env nunca é implícito: o valor do client vale a menos que uma chamada diga o contrário, e só naquela chamada. Existe para quem chama e sabe mais que o contexto do processo — uma execução local lendo o único segredo que mora em outro lugar.

per-call optionsopções por chamada
from secret_kernel.contracts import GetSecretOptions

# /prod/billing/database — the client prefix is left out
await client.get_secret(
    "database", GetSecretOptions(omit_prefix=True)
)

# /prod/shared/api-key — read another project's secret
await client.get_secret(
    "api-key", GetSecretOptions(project="shared")
)

# /staging/billing/database/password — another environment, once
await client.get_secret(
    "database/password", GetSecretOptions(env="staging")
)

# /license-key — belongs to no environment or project
await client.get_secret(
    "license-key",
    GetSecretOptions(omit_env=True, omit_project=True),
)
// /prod/billing/database — the client prefix is left out
await client.getSecret('database', { omitPrefix: true });

// /prod/shared/api-key — read another project's secret
await client.getSecret('api-key', { project: 'shared' });

// /staging/billing/database/password — another environment, once
await client.getSecret('database/password', { env: 'staging' });

// /license-key — belongs to no environment or project
await client.getSecret('license-key', {
    omitEnv: true,
    omitProject: true,
});
05

Reading structured secrets

Lendo segredos estruturados

A secret is a string by default. Ask for a parse mode and the value follows.

Um segredo é uma string por padrão. Peça um modo de parse e o valor será convertido.

Parse modes

Modos de parse

Two modes: JSON and KEY_VALUE. Keys and values are trimmed by default.

Dois modos: JSON e KEY_VALUE. Chaves e valores têm espaços removidos por padrão.

A secret that exists but cannot be parsed raises SecretParseError; parse results are never cached.

Um segredo que existe mas não pode ser parseado levanta SecretParseError; resultados de parse nunca são cacheados.

parseparse
from secret_kernel.contracts import (
    GetSecretOptions,
    SecretParseMode,
)

# "s3cr3t"
plain = await client.get_secret("database/password")

# {"host": "db.internal", "port": 5432}
config = await client.get_secret(
    "database/config", GetSecretOptions(parse=SecretParseMode.JSON)
)

# {"user": "app", "password": "s3cr3t"}
# from "user=app;password=s3cr3t"
pairs = await client.get_secret(
    "database/credentials",
    GetSecretOptions(parse=SecretParseMode.KEY_VALUE),
)
import { SecretParseMode } from '@secret-kernel/contracts';

// "s3cr3t"
const plain = await client.getSecret('database/password');

// { host: 'db.internal', port: 5432 }
type DbConfig = { host: string; port: number };

const config = await client.getSecret<DbConfig>(
    'database/config',
    { parse: SecretParseMode.Json },
);

// { user: 'app', password: 's3cr3t' }
// from "user=app;password=s3cr3t"
const pairs = await client.getSecret(
    'database/credentials',
    { parse: SecretParseMode.KeyValue },
);

Key-value conventions

Convenções chave-valor

If a project always stores key-value secrets the same way, set the convention once on the client instead of repeating it at every call.

Se um projeto sempre guarda segredos chave-valor do mesmo jeito, defina a convenção uma vez no client em vez de repeti-la em cada chamada.

A per-call parse_optionsparseOptions is merged over the client's field by field, so a call that needs one different separator does not have to restate the others.

Um parse_optionsparseOptions por chamada é mesclado sobre o do client campo por campo, então uma chamada que precisa de um separador diferente não precisa repetir os outros.

FieldCampo DefaultPadrão
pair_separatorpairSeparator";"
key_value_separatorkeyValueSeparator"="
keysempty — names positional values that carry no separatorvazio — nomeia valores posicionais que não trazem separador
trimTruetrue

Empty pairs are ignored, empty values are kept as "", an empty key raises, and a repeated key keeps the last value.

Pares vazios são ignorados, valores vazios são mantidos como "", uma chave vazia levanta erro, e uma chave repetida mantém o último valor.

set once on the clientdefinido uma vez no client
from secret_kernel.contracts import KeyValueParseOptions

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-parameter-store",
        env="prod",
        parse_options=KeyValueParseOptions(
            pair_separator="|",
            key_value_separator=":",
        ),
        options=AwsParameterStoreProviderOptions(
            region="us-east-1",
        ),
    )
)

# only the pair separator differs here; ":" is inherited
await client.get_secret(
    "legacy",
    GetSecretOptions(
        parse=SecretParseMode.KEY_VALUE,
        parse_options=KeyValueParseOptions(pair_separator=";"),
    ),
)
const client = await createSecretClient({
    provider: SecretProvider.AwsParameterStore,
    env: 'prod',
    parseOptions: {
        pairSeparator: '|',
        keyValueSeparator: ':',
    },
    options: { region: 'us-east-1' },
});

// only the pair separator differs here; ':' is inherited
await client.getSecret('legacy', {
    parse: SecretParseMode.KeyValue,
    parseOptions: { pairSeparator: ';' },
});
06

Five providers, one call

Cinco cofres, uma chamada

Provider options are typed per provider and never leak into the shared contract.

As opções são tipadas por cofre e nunca vazam para o contrato compartilhado.

ProviderCofre DistributionDistribuição Use forUsar para
in-memory secret-kernel-provider-in-memory@secret-kernel/provider-in-memory Local development, CI and tests. Reads preloaded values or environment variables. Desenvolvimento local, CI e testes. Lê valores pré-carregados ou variáveis de ambiente.
aws-parameter-store secret-kernel-provider-aws-parameter-store@secret-kernel/provider-aws-parameter-store Low-cost secrets in AWS, as SecureString parameters. Segredos de baixo custo na AWS, como parâmetros SecureString.
aws-secrets-manager secret-kernel-provider-aws-secrets-manager@secret-kernel/provider-aws-secrets-manager AWS secrets that need rotation and a dedicated lifecycle. Reads the AWSCURRENT stage; a binary-only secret raises SecretCapabilityNotSupportedError. Segredos da AWS que precisam de rotação e ciclo de vida próprio. Lê o estágio AWSCURRENT; um segredo apenas binário levanta SecretCapabilityNotSupportedError.
gcp-parameter-manager secret-kernel-provider-gcp-parameter-manager@secret-kernel/provider-gcp-parameter-manager Low-cost secrets in GCP. Takes a location, defaulting to global. Segredos de baixo custo no GCP. Recebe um location, com padrão global.
gcp-secret-manager secret-kernel-provider-gcp-secret-manager@secret-kernel/provider-gcp-secret-manager GCP secrets that need rotation and a dedicated lifecycle. Segredos do GCP que precisam de rotação e ciclo de vida próprio.

Defaults

Padrões

region on both AWS providers defaults to us-east-1. The default is the kernel's, not the SDK's: AWS_REGION and the AWS profile are not consulted, so pass the region you mean. location on GCP Parameter Manager defaults to global. project_idprojectId on both GCP providers has no default.

region nos dois cofres da AWS tem padrão us-east-1. O padrão é do kernel, não do SDK: AWS_REGION e o perfil da AWS não são consultados, então passe a região que você quer. location no GCP Parameter Manager tem padrão global. project_idprojectId nos dois cofres do GCP não tem padrão.

A value you pass must say something. Noneundefined or blank text raises SecretKernelConfigurationError instead of falling back to the default, so an unset environment variable fails when the client is built rather than reading from the wrong place.

Um valor que você passa precisa dizer algo. Noneundefined ou texto em branco levanta SecretKernelConfigurationError em vez de cair no padrão, então uma variável de ambiente não definida falha quando o client é construído, e não lendo do lugar errado.

Credentials

Credenciais

Credentials are explicit when you pass them, and fall back to the cloud SDK's own chain when you do not. Explicit credentials take precedence.

As credenciais são explícitas quando você as passa, e caem para a própria cadeia do SDK da nuvem quando você não passa. Credenciais explícitas têm precedência.

Injecting a client keeps the SDK client under your lifecycle — the kernel closes only what it created.

Injetar um client mantém o client do SDK sob o seu ciclo de vida — o kernel fecha apenas o que ele mesmo criou.

Providers that cannot store / flatten the resolved name with a separator: AWS accepts /, _ and - and defaults to /; GCP accepts _ and - and defaults to _.

Cofres que não conseguem guardar / achatam o nome resolvido com um separador: a AWS aceita /, _ e - e usa / como padrão; o GCP aceita _ e - e usa _.

gcp-secret-managergcp-secret-manager
from secret_kernel.provider_gcp_secret_manager import (
    GcpSecretManagerProviderOptions,
)

client = create_secret_client(
    CreateSecretClientConfig(
        provider="gcp-secret-manager",
        env="prod",
        project="billing",
        options=GcpSecretManagerProviderOptions(
            project_id="my-gcp-project",
            physical_name_separator="_",
        ),
    )
)

# reads projects/my-gcp-project/secrets/
#   prod_billing_database_password/versions/latest
const client = await createSecretClient({
    provider: SecretProvider.GcpSecretManager,
    env: 'prod',
    project: 'billing',
    options: {
        projectId: 'my-gcp-project',
        physicalNameSeparator: '_',
    },
});

// reads projects/my-gcp-project/secrets/
//   prod_billing_database_password/versions/latest
07

Caching

Cache

Reads hit the provider every time unless you enable the cache.

As leituras vão ao cofre todas as vezes, a menos que você habilite o cache.

The cache stores the decrypted string keyed by the resolved name, before any parsing, so the same secret read as JSON and as text costs one provider call.

O cache guarda a string descriptografada chaveada pelo nome resolvido, antes de qualquer parse, então o mesmo segredo lido como JSON e como texto custa uma chamada ao cofre.

A value served from cache reports no version and no metadata: the cache stores the string only.

Um valor servido do cache não reporta version nem metadata: o cache guarda apenas a string.

Failures are never cached. A missing secret that appears later is found on the next read.

Falhas nunca são cacheadas. Um segredo ausente que aparece depois é encontrado na leitura seguinte.

Encryption in memory

Criptografia em memória

For a process that holds secrets in memory for a long time, cached values can be encrypted with AES-256-GCM. Without an encryption_keyencryptionKey the client generates one and keeps it in memory only.

Para um processo que mantém segredos em memória por muito tempo, os valores cacheados podem ser criptografados com AES-256-GCM. Sem uma encryption_keyencryptionKey, o client gera uma e a mantém apenas em memória.

What it protects against: a heap snapshot, or an accidental object dump. What it does not: code running inside the same process.

Contra o que protege: um snapshot de heap, ou um dump acidental de objeto. Contra o que não protege: código rodando dentro do mesmo processo.

cachecache
from secret_kernel.contracts import SecretCacheOptions

client = create_secret_client(
    CreateSecretClientConfig(
        provider="aws-secrets-manager",
        env="prod",
        cache=SecretCacheOptions(
            enabled=True,
            ttl_ms=300_000,
            max_entries=100,
            encrypt_in_memory=True,
        ),
        options=AwsSecretsManagerProviderOptions(
            region="us-east-1",
        ),
    )
)
const client = await createSecretClient({
    provider: SecretProvider.AwsSecretsManager,
    env: 'prod',
    cache: {
        enabled: true,
        ttlMs: 300_000,
        maxEntries: 100,
        encryptInMemory: true,
    },
    options: { region: 'us-east-1' },
});
08

Errors are part of the contract

Erros fazem parte do contrato

Every error carries name, code, provider, secret_namesecretName, provider_codeproviderCode and cause.

Todo erro carrega name, code, provider, secret_namesecretName, provider_codeproviderCode e cause.

Three fields identify a failure, and each answers a different question. name is the class, and moves when the class is renamed. code is the kernel's own — scrt_not_found — frozen, always present, and spelled the same in both languages. provider_codeproviderCode is the vendor's — AccessDeniedException on AWS, PERMISSION_DENIED on GCP — and is there only when the SDK gave one.

Três campos identificam uma falha, e cada um responde a uma pergunta diferente. name é a classe, e muda quando a classe é renomeada. code é do próprio kernel — scrt_not_found — congelado, sempre presente e escrito igual nas duas linguagens. provider_codeproviderCode é do fornecedor — AccessDeniedException na AWS, PERMISSION_DENIED no GCP — e só existe quando o SDK deu um.

Catch by class inside your own process. Compare code at a boundary, where a partner writes the string into a comparison of their own and a renamed class would break them in silence.

Capture pela classe dentro do seu próprio processo. Compare code numa fronteira, onde um parceiro escreve a string numa comparação dele e uma classe renomeada o quebraria em silêncio.

Only not-found and permission are normalized. Every other failure keeps the message the SDK produced and gains provider, secret_namesecretName and, when the SDK offered one, provider_codeproviderCode, with the original error in cause.

Só não-encontrado e permissão são normalizados. Toda outra falha preserva a mensagem que o SDK produziu e ganha provider, secret_namesecretName e, quando o SDK ofereceu um, provider_codeproviderCode, com o erro original em cause.

A normalized message names the provider and the secret twice: the name your code asked for, and in parentheses the ID the provider looked up — on GCP, the one to search for in the console.

Uma mensagem normalizada nomeia o cofre e nomeia o segredo duas vezes: o nome que o seu código pediu e, entre parênteses, o ID que o cofre procurou — no GCP, o que você busca no console.

handlingtratamento
from secret_kernel.contracts import (
    SecretKernelError,
    SecretNotFoundError,
)

try:
    secret = await client.get_secret("database/password")
except SecretNotFoundError:
    # in your process: the class
    secret = None
except SecretKernelError as error:
    # at a boundary: the code
    respond(503, {"code": error.code})

# a missing secret, env "prod", on GCP
# str(error)         Secret not found in gcp-secret-manager:
#                    database/password (full name:
#                    prod_database_password)
# error.name         SecretNotFoundError
# error.code         scrt_not_found
# error.secret_name  database/password
import {
    SecretKernelError, SecretNotFoundError,
} from '@secret-kernel/contracts';

let secret;
try {
    secret = await client.getSecret('database/password');
} catch (error) {
    if (error instanceof SecretNotFoundError) {
        // in your process: the class
        secret = undefined;
    } else if (error instanceof SecretKernelError) {
        // at a boundary: the code
        respond(503, { code: error.code });
    } else {
        throw error;
    }
}

// a missing secret, env 'prod', on GCP
// error.message     Secret not found in gcp-secret-manager:
//                   database/password (full name:
//                   prod_database_password)
// error.name        SecretNotFoundError
// error.code        scrt_not_found
// error.secretName  database/password
ClassClasse CodeCódigo Raised whenLevantado quando
SecretNotFoundErrorscrt_not_foundThe secret or parameter does not exist.O segredo ou parâmetro não existe.
SecretPermissionErrorscrt_permission_deniedThe provider refused the read.O cofre recusou a leitura.
SecretParseErrorscrt_parse_failedThe secret was read but the requested parse failed.O segredo foi lido, mas o parse pedido falhou.
InvalidSecretNameErrorscrt_invalid_nameThe resolved name is empty or unusable.O nome resolvido está vazio ou inutilizável.
SecretCapabilityNotSupportedErrorscrt_capability_not_supportedThe provider cannot serve the request under the base contract.O cofre não consegue atender ao pedido dentro do contrato base.
SecretKernelConfigurationErrorscrt_invalid_configurationRequired, invalid or mutually exclusive configuration.Configuração obrigatória, inválida ou mutuamente exclusiva.
SecretProviderErrorscrt_unexpectedAny other provider failure.Qualquer outra falha do cofre.
09

Observability, and your own provider

Observabilidade, e seu próprio cofre

Silent until you attach a logger, and open to a provider the kernel has never heard of.

Silencioso até você anexar um logger, e aberto a um cofre que o kernel nunca viu.

Logging

Logs

Pass a logger and the client writes one plain sentence per event: the secret it is reading, a cache hit or miss, a missing secret at warningwarn, and a refused or failed read at error, carrying the error's own message. No line carries the secret value.

Passe um logger e o client escreve uma frase simples por evento: o segredo que está lendo, um acerto ou falta no cache, um segredo ausente em warningwarn, e uma leitura recusada ou que falhou em error, com a mensagem do próprio erro. Nenhuma linha carrega o valor do segredo.

One string per call, so the standard logger fits as it is — logging.getLogger(...)console, with no adapter.

Uma string por chamada, então o logger padrão serve como está — logging.getLogger(...)console, sem adapter.

The logger is the only sink. Without one the client is silent: there is no debug flag and no console fallback, because where a line lands and what it may expose belongs to the application that attached the logger. A level your logger does not implement is dropped, never raised.

O logger é o único destino. Sem ele, o client fica em silêncio: não existe flag debug nem fallback para o console, porque onde uma linha cai e o que ela pode expor é decisão da aplicação que anexou o logger. Um nível que o seu logger não implementa é descartado, nunca levantado.

Bring your own provider

Traga seu próprio cofre

The factory accepts a provider class directly, so a provider this package has never heard of is used through the same contract. The factory asserts the class declares the requested provider_nameproviderName.

A factory aceita diretamente a classe de um cofre, então um cofre que este pacote nunca viu é usado pelo mesmo contrato. A factory verifica que a classe declara o provider_nameproviderName pedido.

The shared contract suite in the testing package runs every portable guarantee against it — decrypted reads, prefix composition, both parse modes, and SecretNotFoundError on a missing secret.

A suíte de contrato compartilhada no pacote de testing roda todas as garantias portáveis contra ele — leituras descriptografadas, composição de prefix, os dois modos de parse, e SecretNotFoundError num segredo ausente.

logginglogging
import logging

client = create_secret_client(
    CreateSecretClientConfig(
        provider="gcp-secret-manager",
        env="prod",
        logger=logging.getLogger("my-app"),
        options=GcpSecretManagerProviderOptions(
            project_id="my-project",
        ),
    )
)

# DEBUG    Reading secret /prod/database/password
# DEBUG    Reading /prod/database/password from the provider
# WARNING  Secret not found in gcp-secret-manager:
#          database/password (full name: prod_database_password)
const client = await createSecretClient({
    provider: SecretProvider.GcpSecretManager,
    env: 'prod',
    logger: console,
    options: { projectId: 'my-project' },
});

// debug  Reading secret /prod/database/password
// debug  Reading /prod/database/password from the provider
// warn   Secret not found in gcp-secret-manager:
//        database/password (full name: prod_database_password)
custom providercofre próprio
create_secret_client(
    CreateSecretClientConfig(
        provider="my-vault",
        provider_class=MyVaultClient,
    )
)
await createSecretClient({
    provider: 'my-vault',
    providerClass: MyVaultClient,
});
10

What it refuses to do

O que ele se recusa a fazer

The scope is the feature. A narrow contract is one you can reason about.

O escopo é a feature. Um contrato estreito é um contrato sobre o qual você consegue raciocinar.

  • Read-only. No write, no delete, no rotation, no batch read, no explicit historical version.
  • Latest version only. When a provider supports versions, the kernel always reads the current one.
  • Cross-environment reads are opt-in, one call at a time. The client's env applies unless a call says otherwise. Replacing it is never implicit and never a default, and omit_envomitEnv still wins over it — a service reading another environment is usually a defect, but the caller sometimes knows better than the process context.
  • Provider options never leak. region belongs to AWS, project_idprojectId to GCP. The shared contract knows neither.
  • Failures are never cached. A missing secret that appears later is found on the next read.
  • Somente leitura. Sem escrita, remoção, rotação, leitura em lote ou versão histórica explícita.
  • Apenas a última versão. Quando o cofre suporta versões, o kernel sempre lê a atual.
  • Leitura entre ambientes é opt-in, uma chamada por vez. O env do client vale a menos que uma chamada diga o contrário. Substituí-lo nunca é implícito nem default, e omit_envomitEnv continua vencendo — um serviço lendo outro ambiente é normalmente um defeito, mas quem chama às vezes sabe mais que o contexto do processo.
  • Opções do cofre não vazam. region é da AWS, project_idprojectId é do GCP. O contrato compartilhado não conhece nenhuma das duas.
  • Falhas nunca são cacheadas. Um segredo ausente que aparece depois é encontrado na leitura seguinte.

Writing, rotating and versioning stay outside this contract by design.

Escrita, rotação e versionamento ficam fora deste contrato por decisão de projeto.

11

Changelog

Changelog

Early, and honest about it. Each implementation releases on its own, so this follows the language you picked above. Within one release every distribution shares a version.

No começo, e honesto sobre isso. Cada implementação lança por conta própria, então isto acompanha a linguagem escolhida acima. Dentro de um release, toda distribuição compartilha a versão.

0.1.0a7 2026-09-25 tagged, not on PyPI yet tagueado, ainda fora do PyPI

Changed · breaking

Alterado · breaking

  • code changed owner. It held the vendor's identifier, which moved to provider_code. code is now the kernel's own, always present and frozen: scrt_not_found, scrt_permission_denied, scrt_parse_failed, scrt_invalid_name, scrt_capability_not_supported, scrt_invalid_configuration, scrt_unexpected. SecretErrorCode is a StrEnum of those seven.
  • A normalized failure names the secret you asked for and, in parentheses, the ID the provider looked up: database/password (full name: dev_billing_database_password).
  • Errors are built from one SecretKernelErrorOptions, and resolve_secret_full_name takes (client_options, secret_name, options) — the same call shape as TypeScript.
  • Helpers drop the secret prefix where the rest of the name is already this kernel's vocabulary — normalize_provider_error, assert_client_contract and six more. Breaking for a custom provider importing them.
  • replace_secret_path_separator never returns a leading separator. The AWS providers keep the resolved name themselves, so what they send is unchanged.
  • SecretConfigurationError is now SecretKernelConfigurationError, with no alias. Its code stays scrt_invalid_configuration.
  • region on both AWS providers defaults to us-east-1, and AWS_REGION or the AWS profile is no longer consulted. Breaking for a caller who left it out and relied on boto3 to find it. location keeps its global default.
  • region, location or project_id given as None or blank text raises SecretKernelConfigurationError instead of falling back to the default.
  • code mudou de dono. Ele guardava o identificador do fornecedor, que foi para provider_code. code agora é do próprio kernel, sempre presente e congelado: scrt_not_found, scrt_permission_denied, scrt_parse_failed, scrt_invalid_name, scrt_capability_not_supported, scrt_invalid_configuration, scrt_unexpected. SecretErrorCode é um StrEnum desses sete.
  • Uma falha normalizada nomeia o segredo que você pediu e, entre parênteses, o ID que o cofre procurou: database/password (full name: dev_billing_database_password).
  • Erros são construídos a partir de um único SecretKernelErrorOptions, e resolve_secret_full_name recebe (client_options, secret_name, options) — o mesmo formato de chamada do TypeScript.
  • Helpers perdem o prefixo secret onde o resto do nome já é vocabulário deste kernel — normalize_provider_error, assert_client_contract e mais seis. Breaking para um cofre próprio que os importa.
  • replace_secret_path_separator nunca devolve um separador inicial. Os cofres da AWS mantêm o nome resolvido por conta própria, então o que eles enviam não muda.
  • SecretConfigurationError agora é SecretKernelConfigurationError, sem alias. O code continua scrt_invalid_configuration.
  • region nos dois cofres da AWS tem padrão us-east-1, e AWS_REGION ou o perfil da AWS deixam de ser consultados. Breaking para quem omitia a região e contava com o boto3 para achá-la. location mantém o padrão global.
  • region, location ou project_id passados como None ou texto em branco levantam SecretKernelConfigurationError em vez de cair no padrão.

Changed

Alterado

  • Every third-party dependency declares a lower bound, the current release that supports Python 3.12; the three cloud SDKs had none.
  • Toda dependência de terceiros declara um limite inferior, o release atual que suporta Python 3.12; os três SDKs de nuvem não tinham nenhum.

Fixed

Corrigido

  • Configuration errors name the provider by its canonical name — aws-parameter-store, not AWS Parameter Store — in the message and in provider.
  • Erros de configuração nomeiam o cofre pelo nome canônico — aws-parameter-store, não AWS Parameter Store — na mensagem e em provider.
0.1.0a6 2026-08-29 tagged, not on PyPI yet tagueado, ainda fora do PyPI

Changed · breaking

Alterado · breaking

  • A normalized failure names the provider and the secret: Permission denied for aws-secrets-manager secret: /dev/billing/database/password, where it said Secret provider denied the read.
  • A log line is one string, and the logger is the only sink. The context argument and the debug option are gone, and failures are logged — a missing secret at warning, a refused or failed read at error. logging.getLogger("my-app") is a drop-in.
  • Every error carries its class name in name.
  • The public surface matches TypeScript name for name: DEFAULT_CLIENT_CONFIG, AwsSecretsManagerClient, SecretProvider. The contract suite exports only assert_secret_client_contract and CreateClient.
  • Uma falha normalizada nomeia o cofre e o segredo: Permission denied for aws-secrets-manager secret: /dev/billing/database/password, onde dizia Secret provider denied the read.
  • Uma linha de log é uma string, e o logger é o único destino. O argumento de contexto e a opção debug saíram, e falhas são logadas — um segredo ausente em warning, uma leitura recusada ou que falhou em error. logging.getLogger("my-app") serve direto.
  • Todo erro carrega o nome da sua classe em name.
  • A superfície pública bate com a do TypeScript nome a nome: DEFAULT_CLIENT_CONFIG, AwsSecretsManagerClient, SecretProvider. A suíte de contrato exporta só assert_secret_client_contract e CreateClient.

Added

Adicionado

  • The factory declares one extra per provider, plus all, and no longer depends on secret-kernel-core.
  • A factory declara um extra por cofre, mais all, e não depende mais de secret-kernel-core.
0.1.0a4 2026-08-27

Changed

Alterado

The Changelog link on the package page points here instead of at the raw file on GitHub, so every link on that page leads to where the reader already is.

O link Changelog na página do pacote aponta para cá em vez do arquivo bruto no GitHub, então todo link daquela página leva para onde quem lê já está.

0.1.0a3 2026-08-27

Added

Adicionado

get_secret accepts an env that replaces the client's environment for one call. It is never implicit and never a default: the environment the client was built with applies unless a call says otherwise, in that call alone, and omit_env still wins over it.

get_secret aceita um env que substitui o ambiente do client por uma chamada. Nunca é implícito nem default: o ambiente com que o client foi construído vale a menos que uma chamada diga o contrário, e só naquela chamada, e o omit_env continua vencendo.

0.1.0a2 2026-08-25

Changed

Alterado

  • The API reference guard now checks both directions. It asserted that every export is documented and never that every documented name still exists, so a name that left the public surface stayed in the reference indefinitely.
  • The integration suites hand credentials to the provider instead of expecting them in the environment, the same way an application injects configuration — which also exercises the documented precedence of explicit credentials over the SDK chain.
  • O guard da referência de API agora checa nas duas direções. Ele afirmava que todo export está documentado e nunca que todo nome documentado ainda existe, então um nome que saiu da superfície pública ficava na referência indefinidamente.
  • As suítes de integração passam credenciais ao cofre em vez de esperá-las no ambiente, do mesmo jeito que uma aplicação injeta configuração — o que também exercita a precedência documentada de credenciais explícitas sobre a cadeia do SDK.
0.1.0a1 first alpha primeiro alpha

The contract, the shared core, the factory, the five providers and the shared contract suite. Tagged before changelog fragments were being kept, which is why there is nothing itemised here.

O contrato, o core compartilhado, a factory, os cinco cofres e a suíte de contrato compartilhada. Tagueado antes de os fragmentos de changelog começarem a ser mantidos, e é por isso que não há itens detalhados aqui.

0.1.0-alpha.7 tagged, not on npm yet tagueado, ainda fora do npm

Changed · breaking

Alterado · breaking

  • code changed owner. It held the vendor's identifier, which moved to providerCode. code is now the kernel's own, always present and frozen — the same seven scrt_* values as Python. SecretErrorCode is the constant map of them and the union of its values.
  • A normalized failure names the secret you asked for and, in parentheses, the ID the provider looked up: database/password (full name: dev_billing_database_password).
  • close() is required on SecretProviderReader and SecretClient, as it already was in Python.
  • Helpers drop the Secret prefix where the rest of the name is already this kernel's vocabulary — normalizeProviderError, defineProviderContractTests and six more. Breaking for a custom provider importing them.
  • SecretConfigurationError is now SecretKernelConfigurationError, with no alias. Its code stays scrt_invalid_configuration.
  • region on both AWS providers may be omitted and defaults to us-east-1; location on GCP Parameter Manager may be omitted and defaults to global. Both were required. The default is the kernel's: AWS_REGION is not consulted.
  • region, location or projectId given as undefined, null or blank text throws SecretKernelConfigurationError instead of falling back to the default.
  • code mudou de dono. Ele guardava o identificador do fornecedor, que foi para providerCode. code agora é do próprio kernel, sempre presente e congelado — os mesmos sete valores scrt_* do Python. SecretErrorCode é o mapa constante deles e a união dos seus valores.
  • Uma falha normalizada nomeia o segredo que você pediu e, entre parênteses, o ID que o cofre procurou: database/password (full name: dev_billing_database_password).
  • close() é obrigatório em SecretProviderReader e SecretClient, como já era no Python.
  • Helpers perdem o prefixo Secret onde o resto do nome já é vocabulário deste kernel — normalizeProviderError, defineProviderContractTests e mais seis. Breaking para um cofre próprio que os importa.
  • SecretConfigurationError agora é SecretKernelConfigurationError, sem alias. O code continua scrt_invalid_configuration.
  • region nos dois cofres da AWS pode ser omitido e tem padrão us-east-1; location no GCP Parameter Manager pode ser omitido e tem padrão global. Os dois eram obrigatórios. O padrão é do kernel: AWS_REGION não é consultado.
  • region, location ou projectId passados como undefined, null ou texto em branco lançam SecretKernelConfigurationError em vez de cair no padrão.

Fixed

Corrigido

  • A cache at maxEntries no longer evicts an unrelated secret when one it already holds is read again.
  • Configuration errors name the provider by its canonical name, and a GCP provider created without options throws SecretKernelConfigurationError instead of a plain Error.
  • Um cache em maxEntries não descarta mais um segredo não relacionado quando um que ele já guarda é lido de novo.
  • Erros de configuração nomeiam o cofre pelo nome canônico, e um cofre do GCP criado sem opções lança SecretKernelConfigurationError em vez de um Error comum.
0.1.0-alpha.6 tagged, not on npm yet tagueado, ainda fora do npm

There is no 0.1.0-alpha.5: the number was skipped so this release matches 0.1.0a6 in Python.

Não existe 0.1.0-alpha.5: o número foi pulado para que este release case com o 0.1.0a6 do Python.

Changed · breaking

Alterado · breaking

  • Provider failures are wrapped instead of rethrown raw. Every failure other than not-found and permission keeps the SDK's message and gains provider, secretName and code, with the original error in cause — the model Python always had.
  • A binary-only secret raises SecretCapabilityNotSupportedError instead of a plain Error.
  • A normalized failure names the provider and the secret: Permission denied for aws-secrets-manager secret: /dev/billing/database/password.
  • A log line is one string, and the logger is the only sink. The context argument and the debug option are gone, and failures are logged — a missing secret at warn, a refused or failed read at error. logger: console is a drop-in.
  • The createClient alias is gone from every provider, and SecretMetadata names the shape of metadata.
  • Falhas do cofre são embrulhadas em vez de relançadas cruas. Toda falha que não seja não-encontrado ou permissão preserva a mensagem do SDK e ganha provider, secretName e code, com o erro original em cause — o modelo que o Python sempre teve.
  • Um segredo apenas binário levanta SecretCapabilityNotSupportedError em vez de um Error comum.
  • Uma falha normalizada nomeia o cofre e o segredo: Permission denied for aws-secrets-manager secret: /dev/billing/database/password.
  • Uma linha de log é uma string, e o logger é o único destino. O argumento de contexto e a opção debug saíram, e falhas são logadas — um segredo ausente em warn, uma leitura recusada ou que falhou em error. logger: console serve direto.
  • O alias createClient saiu de todos os pacotes de cofre, e SecretMetadata nomeia o formato de metadata.
0.1.0-alpha.4

Changed

Alterado

The homepage carries the language, so a reader arriving from the npm page lands on the TypeScript tab rather than on whichever one the site defaults to. The site reads that from the query ahead of a preference already stored in the browser, so the link decides even for a returning visitor. No source changes.

O homepage carrega a linguagem, então quem chega da página do npm cai na aba TypeScript em vez da que o site usa por padrão. O site lê isso da query antes de uma preferência já guardada no navegador, então o link decide mesmo para quem já visitou. Sem mudanças de código.

0.1.0-alpha.3

Added

Adicionado

getSecret accepts an env that replaces the client's environment for one call. It is never implicit and never a default: the environment the client was built with applies unless a call says otherwise, in that call alone, and omitEnv still wins over it.

getSecret aceita um env que substitui o ambiente do client por uma chamada. Nunca é implícito nem default: o ambiente com que o client foi construído vale a menos que uma chamada diga o contrário, e só naquela chamada, e o omitEnv continua vencendo.

0.1.0-alpha.1 first alpha primeiro alpha

The contract, the shared core, the factory, the five providers and the shared contract suite. Tagged before changelog fragments were being kept, which is why there is nothing itemised here.

O contrato, o core compartilhado, a factory, os cinco cofres e a suíte de contrato compartilhada. Tagueado antes de os fragmentos de changelog começarem a ser mantidos, e é por isso que não há itens detalhados aqui.