Skip to content
Chock
Credentials (22 of 23)

Credentials

chock login --provider aiand
chock login --provider anthropic --name work
chock login --provider openai-compat=http://127.0.0.1:5000/v1 --name local
printf '%s' "$KEY" | chock login --provider aiand --password-method stdin

The credential goes to your operating system’s own keystore: the secret service on Linux, which is what gnome-keyring and kwallet answer, and the Keychain on macOS. It is checked against the provider before it is stored, so a mistyped key fails at login rather than on your first turn.

Where a credential is kept

Name the store in ~/.config/chock/config.zon:

.credentials = .{ .store = "secret_service" },
Store What it is Where
secret_service the freedesktop secret service, over the session bus Linux, and the default there
keychain the macOS Keychain macOS, and the only store there
file a file in ~/.local/share/chock, mode 0600 in a directory 0700 Linux
secretspec SecretSpec, asked over its own protocol every platform

Nothing is guessed, and nothing falls back. A keystore can look reachable and still be unusable: on a machine with no desktop session the bus is there, the service starts when asked, and opening a session works, and then the collection is locked and the prompt that would unlock it cannot be drawn. So a store that cannot work is an error that says so, and never a quiet move to somewhere less protected. Naming a store this platform does not have is refused when the file is read, not at your first turn.

secretspec

SecretSpec declares what secrets a project needs, separately from where they live, and fetches them from whichever of its own backends you configured: keyring, 1Password, Vault, AWS, SOPS and about thirty more. Naming it here means your Chock credential comes from wherever you already keep your secrets, and Chock implements none of those backends itself.

.credentials = .{ .store = "secretspec" },

Chock runs secretspec serve and asks it, so secretspec has to be on your PATH. Reading a credential runs it in its own read only mode, so a session that only needs a key cannot change one.

A secret it does not have is not an error. Chock reads that as “nobody has stored this yet”, the same as every other store, so chock login is what fills it. What a login cannot fix is a secret your secretspec.toml does not declare, or a SecretSpec configured read only. Both refuse, and Chock repeats what SecretSpec said about it rather than inventing a reason of its own.

A machine you only reach over ssh usually wants file. It has no desktop session to unlock a collection with. The file is mode 0600 in a directory 0700, so it is readable by your account and no other, which is the same guarantee the secret service gives against other accounts. What the keystore adds is encryption at rest and being locked when your session is locked.

--password-method says where the credential comes from: prompt is a hidden prompt and the default at a terminal, stdin is one line of standard input and the default otherwise, and file=<path> reads it from a file.

There is no option that takes the credential as an argument, and no environment variable for one. A command line is visible to every other user through ps and it lands in your shell history.

Two logins at once

chock login takes a lock while it writes, and it holds that lock over the credential and the index together, so a second login that starts while the first is writing waits for it. One login writes both files or neither. The wait is five seconds, which is thousands of times what a store write takes.

If that runs out, the second login says another chock login is running and stores nothing. Wait for the first one to finish and run it again. The message never names the credential or the instance, because you read it off a terminal other people can see.

The lock is the kernel’s own, so a login you kill releases it at once. There is nothing to clean up by hand.

The name of an instance you did not name is its kind, so a second chock login --provider aiand is a second login for one name. At a terminal it asks before it replaces anything, and no is the default:

chock login: there is already a credential named "aiand", stored as kind aiand.
Replace it? [y/N]

The question comes before the credential is read, so answering no costs you nothing and you never type a secret for a login that will not happen.

A run with nobody to ask is refused instead, because silence is not a yes. That is a pipe, a --password-method that reads a file, and any continuous integration job. Those say which they meant:

chock login --provider aiand --replace
chock login --provider aiand --name <a name of your own>

--replace also works at a terminal, and skips the question.

The same check is made again while the index lock is held, so a login that started while another one sat at its prompt is told the name is taken rather than writing over what that one stored.

On macOS the lock is also held while the Keychain is written. The Keychain is never allowed to ask you anything, so it cannot sit waiting: a locked one is refused at once, with a message naming what to do about it. Unlock it with security unlock-keychain and run the login again.

An account reached only over ssh has never had a desktop login, so it has no login keychain at all. That is a different problem and unlocking cannot fix it. Chock says which of the two it met.

Where a lookup goes

A session that needs a credential asks for the instance by name and takes the first of these that answers:

Path Owner
1. the instance’s own token or token_file the configuration directory you, or home-manager
2. tokens.zon, a name to a token the configuration directory you, always
3. what chock login wrote the data directory Chock, always

First match wins, and a lookup that finds nothing sends no credential. That last case is not a failure. It is what a local llama.cpp server needs, and it is the reason there is no none spelling and no placeholder string pretending to be a secret.

chock run still refuses one shape of it, before it builds a workspace or opens a log: an instance of kind anthropic or aiand that still talks to that kind’s own address, with nothing in any of the three sources. Those two addresses refuse every request that carries no credential, so the session can only end in a 401 after the setup is already done. The refusal names the instance and the address, never any part of a value. An instance of either kind pointed somewhere else keeps running with no credential, because Chock knows nothing about that address.

Sources 2 and 3 stay two different files even though both hold tokens. Chock rewrites 3 and must never rewrite 2, because a program that edits a file a person maintains will one day reformat it, drop a comment, or lose an entry.

All three sources are checked for their mode. A token file that another user can read is refused by name. .token written in place is allowed because of that check, not in spite of it.

The store is never in the sandbox

The credential store is a file in the data directory, and nothing mounts that directory into a sandbox. Two per project subdirectories below it are mounted, the knowledgebase and the toolchain cache, and binding a subdirectory does not expose its parent. So no tool call, no shell command and no program the agent runs can read the file your key is in.

The session’s own environment is built the same way. HOME from your shell never reaches a tool call, which is what keeps a key in your own environment away from the agent. toolchains.md says what is put in its place.