Skip to content

Credentials

Most useful tools need a secret. The awkward part is not storing it — it is that everything between you and the component is a place the secret should not end up: the agent’s context window, a committed config file, a process listing, a log.

ACT’s answer has two halves. Sessions decide when a credential crosses the boundary — once, at open, rather than on every call. The credential store decides where it lives until then — on the host, never in the component’s manifest and never in the agent’s view.

Provision once; every later run finds it.

Terminal window
act login actpkg.dev/library/some-api

act login reads what the component declares it needs and prompts for each field. If the component declares exactly one credential you can omit --key; if it declares several, name one. An existing credential is reported and left alone unless you pass --force.

For anything non-interactive, act secret set is the same store without the prompting:

Terminal window
act secret set actpkg.dev/library/some-api --field acme:token
act secret set actpkg.dev/library/some-api --field acme:token --fields-stdin < token.json
act secret set actpkg.dev/library/some-api --field acme:token --from-command 'vault read -field=json secret/acme'
Terminal window
act secret list # key, kind, description, expiry — never a value
act secret rm <component> --key <key>

The component reference is the namespace: a credential stored for actpkg.dev/library/some-api is not visible to any other component. The backing store defaults to the platform’s credential location; --credentials-backend file:<path> points both act secret and act run at the same alternative, so the writer and the reader cannot drift apart.

There is no fixed schema. A credential is whatever fields you say it is:

Terminal window
act secret set <component> --field acme:username --field acme:password # a password
act secret set <component> --field acme:token # an API token

No field name is well-known — whoever stores the credential names it, in their own namespace. The std: namespace belongs to the spec and is refused here. A few std: names are reserved for components to ask for, when a component wants to accept a conventional shape:

KeyUse
std:bearer-tokenOAuth/OIDC bearer, or a generic bearer-style token
std:api-keyAPI key — the component decides which header it becomes upstream
std:username / std:passwordBasic auth

Anything component-specific takes a vendor prefix: pg:sslcert, anthropic:org-id. When a value is not a plain string, state its type: --field acme:token=std:oauth2.

A component declares what it expects, which is what makes act login able to prompt for it:

# The class carries no constraints, but an undeclared class is denied outright —
# without this line the component cannot reach the store however it is run.
[std.capabilities."act:credentials"]
[[std.credentials]]
key = "default" # what a session uses when its args name no key
description = "Acme API user"
[[std.credentials.fields]]
key = "acme:token"
label = "Acme API token"

act-build pack refuses a manifest that declares [[std.credentials]] without the act:credentials capability — the two always travel together.

At run time the component imports act:credentials/store and asks for what it needs, scoped to its session:

list-secrets: async func(session: option<string>) -> result<list<secret-info>, secret-error>;
get-secret: async func(session: string, want: secret-request) -> result<secret, secret-error>;

Note the direction: this is a host import. The host implements the store; the component asks. A component never sees a filesystem path, a keyring, or another component’s secrets — only the answer to its own request.

Not per-call metadata. A credential repeated on every invocation is a credential in the agent’s context on every invocation. The spec discourages it; use session args.

Not env in .mcp.json. That file is committed — that is the whole point of it — so a secret in it is a secret in the repository. See wiring a component into your agent.

Not the config file. ~/.config/act/config.toml is for grants and paths. Credentials rotate and should not live where policy lives.