Status: Implementation IMPLEMENTED × Exposure PUBLIC PREVIEW. Vault:
cmd/allternit-api/src/aci_credentials.rs. Contract member: use_credential in allternit.computer.v2 (see Computer toolset). Resolution and typing: cmd/allternit-api/src/computer_v2.rs.Overview
Agents sometimes have to type secrets: a password into a login form, a TOTP code into a 2FA prompt.use_credential does this without the value ever entering the model context, the prompt, the tool result or the logs. The model sees and sends only the credential’s name; the executor resolves the value server-side and types it into the focused field, and the audit row records the use with the value redacted.
One contract member:
On non-sandbox targets (this-device, paired computers) a person approves each use, the same rule as the
type member; sandboxed cloud computers run it under the run’s policy.
Backends
The schema endpoint (GET /api/v1/computers/:id/toolset/schema) reports credential_backends with what answers on the current machine.
Resolution order with no
source: vault first, then Keychain. A miss in both answers a plain “no credential named …” error — the error path carries names only, never values.
Bindings: a credential types only into its app
A credential can be bound to an app or domain at creation:use_credential then refuses to type it anywhere else: pass domain equal to the binding or the call fails with “bound to ‘github.com’, not …”. The safety layer also checks the app or page the agent is really typing into, where the executor can see it (this Mac through the Allternit Driver, Linux cloud computers by the active window, the gateway browser by its tab URL). It screens every free-text path for vault secrets: a bound secret typed outside its binding through type, act, run_batch, form_input or run_subtask inputs is refused, as is one typed where the binding can’t be confirmed.
bind accepts ASCII alphanumerics plus ._-:/ (up to 128 chars), so hostnames (github.com), bundle-style ids (com.apple.Safari) and URL prefixes all work. GET /api/aci/credentials/:name returns the binding; values are never returned by any endpoint.
TOTP
{ "kind": "totp" } generates a fresh 6-digit RFC 6238 code (HMAC-SHA-1, 30-second steps) from a totp_secret credential — the same POST /api/aci/credentials type with a base32 seed — and types the code. The seed is never typed and never injected into a sandbox environment; only the derived code leaves the vault, once. Codes can also be fetched without typing via GET /api/aci/credentials/:name/totp.
Where the value flows (and where it does not)
- The model sends only the credential name in the tool call.
- The executor opens the value in memory (
CredentialStore::open_for_computer_use) — it is never serialized, and the store’sDebugis intentionally not derived. - Typing rides the local driver socket (this-device) or the guest’s exec channel, base64-wrapped into a temp file that is removed immediately (guests).
- The driver-side router audit logs op/app/engine timings only — never tool arguments.
- The result text is “Credential ‘<name>’ typed into the focused field.” The policy audit row records the member, risk, target and computer — no value.
Human approval
use_credential on this-device returns 409 approval_required the first time in a run; the normal approval round trip (POST /api/aci/handoff/:approval_id/approve, then resend with approval_grant) covers it, and gizzi’s adapter drives that round trip automatically. The grant binds to the exact action hash, so approving “type github-login into github.com” cannot approve anything else.