orbotodocs
Admin guideSecurity policies

Secrets and credentials

Why a secret never belongs in a doc, ticket or comment: the write guard that refuses credential-shaped values, the sweep for existing content, and the encrypted credential vault with grants, reveal audit and [[secret:KEY]] references.

orboto keeps two things apart: content that people and agents write, and the secrets that content sometimes needs to point at. Content is searched, embedded, exported and kept in revision history, so a password pasted into a doc spreads further than its author expects. The secret guard stops that at the write, and the credential vault gives the value a home with explicit access and an audit trail.

Secret guard

Added in v0.209.0

Every content write in orboto - doc create, edit and append, knowledge add, the wiki's plan-apply and section-append, URL and file ingest, ticket and doc comments, a ticket's description and customer summary, and primer facts - runs through a pattern detector before it reaches the database. A value that looks like a credential (an orboto API key, a GitHub, AWS, Slack, Stripe, SendGrid, Resend, OpenAI or Anthropic key, a private-key block, a JWT, a user:pass@host URL, a labelled password: / token: / secret: value, the secret= of an otpauth:// link, a base32 TOTP seed after a TOTP:, 2FA: or MFA: label, or a block of 8 to 12 recovery codes, one per line and all in the same format) is refused with a 422 and never reaches storage, the search index or AI embeddings. Placeholders such as <your-token>, $VAR, ***, example or changeme are not flagged, and a sentence naming where a credential lives ("Passwort: siehe 1Password") passes through unchanged - the guard blocks the value, never the reference to it. An SSH public key line (ssh-ed25519 AAAA...) is not a secret and passes. The orboto CLI and the agent skill wrapper run the same detector locally, so a secret never leaves the machine in the first place.

Content written before this guard existed is found too: the wiki lint pass flags a page that already holds a secret-shaped value, and a super-admin can run a sweep (Admin -> Security -> Secret scan, or orboto admin secret-scan) over docs, revisions, comments, tickets, ticket history and primer facts. A hit shows only a masked preview, never the value; redacting replaces the value with a <redacted YYYY-MM-DD> marker, purges the revision history, deletes the affected embedding chunks and re-embeds the cleaned content.

If a doc or a ticket genuinely needs to point at a credential, store it in the credential vault below and reference it with a [[secret:KEY]] token instead of the value itself.

Move a secret from content into the vault

Added in v0.211.0 The secret scan (Admin -> Security -> Secret scan) finds passwords, keys and tokens that were stored in docs, comments, tickets or primer facts before the secret guard existed. For each finding it can read, orboto proposes the credential it would become:

  • a login: a username or email next to a password. A TOTP seed in the same block becomes the login's second factor
  • recovery codes: a block of 8 to 12 codes
  • an SSH key pair: a private key and the public key that follows it. A private key alone is stored as an SSH key
  • a certificate: a PEM certificate next to its private key
  • an API key, a token or a password

Click Move into the vault on the finding. The dialog shows what orboto read, with every secret masked, the project (taken from the ticket or doc, and changeable), a key and a title. Confirm, and orboto does it all in one step: it creates the credential and replaces the value in the content with [[secret:KEY]]. For docs and comments it also removes the old revisions that still held the value. If any part fails, nothing changes. If the content was edited after the scan, orboto refuses with a conflict; scan again and use the new finding. Copies in ticket activity snapshots keep the value and stay redact-only, like copies in search previews. The audit log records the move with the finding.

A TOTP seed without a login beside it is added to an existing login: enter that login's key. Findings orboto cannot read as a credential keep the Redact only action.

You need super-admin rights and secret:manage in the target project. Nothing is moved automatically; the scan only makes proposals. After a move, rotate the credential: it was readable in the content until now.

orboto admin secret-scan                                   # list findings; each offer prints its move command
orboto admin secret-scan move FINDING_ID --dry-run         # what would be created, secrets masked, nothing written
orboto admin secret-scan move FINDING_ID --key acme-login --project ACME

Credential vault

Added in v0.209.0

Secrets - passwords, API keys, tokens, SSH keys, certificates, free-form notes - live in an encrypted vault, never in a doc, a ticket or a comment. A write that looks like a secret is refused there and told to use a credential reference instead.

Storing a credential

Create a credential from Security -> Credentials (workspace-scoped, super-admin) or a project's Settings -> Credentials tab (project-scoped, project-admin), the CLI (orboto credential create), or the REST API. The value is encrypted at rest with the same AES-256-GCM envelope used for git tokens and BYOK keys, and is never written to full-text search, embeddings, exports or the activity feed.

Credential kinds

Added in v0.211.0 A credential has a kind, and each kind keeps the fields that belong together in one place:

  • Login - username, password, the address of the site, and optionally the TOTP secret of its second factor, its recovery codes and the second factors orboto does not hold. For the TOTP secret, paste the base32 secret or the otpauth:// link, or paste, drop or pick the QR code image the site shows. The image is read in your browser and never uploaded. orboto shows the issuer and the account it found so you can check them before you save. The secret is never shown again: whoever may reveal the login gets the current code instead.
  • SSH key pair - private key, public key and an optional passphrase. orboto computes the fingerprint (SHA256:..., the same value ssh-keygen -lf prints) when you save.
  • Certificate - the PEM certificate, optionally its chain and private key. orboto reads the expiry date when you save and shows it as a badge: red once it has expired, amber within 30 days.
  • File - any file up to 1 MB, such as a password database, a keystore or a kubeconfig. It is encrypted with the same key as every other secret before it is stored. Downloading it counts as a reveal.
  • Recovery codes - a list of one-time codes, each with a used marker. The list shows how many are left and warns when fewer than three unused codes remain.
  • Passkey - created by the orboto browser, see Passkeys.
  • Password, API key, token, SSH key, note - a single value, as before. Existing credentials keep their kind and keep working unchanged.

Anyone who can see a credential sees its kind, username, address, public key, fingerprint, expiry date, file name and size, and how many recovery codes are left - never a secret. Revealing shows the secret fields. Marking a recovery code as used needs the same reveal access.

One-time codes

Added in v0.211.0 A login with a TOTP secret has a Code button on its [[secret:KEY]] chip and on the credential page. Whoever may reveal the login clicks it and gets the current code with the seconds it stays valid. Click the code to copy it. It disappears when it runs out, and orboto never fetches the next one on its own: click again for a new code. Every click counts as a reveal of the credential with the purpose "TOTP code" in its reveal history. Anyone who may only see the credential reads TOTP set and gets no code.

From the command line, orboto credential totp KEY prints the current code. orboto credential create KEY --kind login --totp-qr qr.png (or orboto credential update KEY --totp-qr qr.png) reads the QR code image on your computer and sends only the secret and its settings, never the image.

Second factors orboto does not hold

Added in v0.211.0 Some logins also need a hardware key, a passkey on a device, an SMS, an email or a push approval on a phone. orboto cannot answer these, but it can record them. Add them to the login under Other second factors, each with a note such as "YubiKey in the office safe", or run orboto credential update KEY --external-factor 'hardware_key:YubiKey in the office safe' (repeat the flag for more than one; --clear-external-factors removes them). The kinds are hardware_key, platform_passkey, sms, email and app_push. Everyone who can see the credential reads the list.

When the orboto browser logs in with the credential and the site shows a prompt for one of the recorded factors, the login stops there. The agent is told which factor the site wants and what your note says, for example the site asks for a second factor the vault does not hold - recorded: hardware key (YubiKey in the office safe), so it can hand the step to a person. This does not count as a failed login.

Key pairs generated in your browser

Added in v0.211.0 Generate in browser on an SSH key pair creates a new Ed25519 key on your machine. If you enter a passphrase first, the private key is encrypted with it the same way ssh-keygen does it. Both halves are stored in one step. The private key is never shown again after it is generated. You can copy the public key and see its fingerprint, and orboto checks that the fingerprint it computes when you save is the same; if they differ, it deletes the credential again and tells you. If your browser cannot create Ed25519 keys, orboto says so: create the pair with ssh-keygen -t ed25519 and paste both halves.

Using an SSH key pair

Added in v0.211.0 An SSH key pair from the vault can be used without ever saving the private key as a file. orboto credential ssh-agent deploy-key starts a new ssh-agent, loads the key into it and prints the lines your shell needs:

eval "$(orboto credential ssh-agent deploy-key)"
git clone git@git.example.org:team/app.git

The private key goes straight into the agent, and a passphrase is answered once and never written down. The agent keeps the key for 8 hours; --ttl 1h shortens that, and orboto credential ssh-agent --stop deploy-key ends it at once. orboto credential ssh-agent deploy-key --print-public shows the public key, for example to add it to a server; that needs only view access and does not count as a reveal.

For a single connection, orboto ssh --credential deploy-key deploy@host.example.org uptime loads the key, runs ssh and stops the agent when ssh ends. orboto claude --ssh-credential deploy-key (also codex, gemini, kimi and ollama) keeps the agent for the whole session.

For a lane. Pick the key pair under SSH key pair in the lane's settings on Admin -> Agent lanes, or run orboto lanes assign review-1 --ssh-credential deploy-key. The lane's runner loads the key for every job, uses it for its git checkouts and the agent's own git calls, and stops it when the job ends, also when the job fails. The lane's agent account needs reveal access to the credential: orboto credential grant deploy-key --user AGENT_ACCOUNT --level reveal.

Every load counts as a reveal with the purpose "SSH agent". For a lane the reveal history also names the lane and the job.

Files

Added in v0.211.0 Drop a file onto the file field or choose one. orboto shows the 1 MB limit before anything is uploaded and shows the upload progress. Downloading asks first, because a download counts as a reveal and is recorded in the audit log.

Rotation reminders

Added in v0.211.0 Every credential can remind you to rotate it. New credentials start with a default for their kind: passwords and logins 90 days, API keys and tokens 180 days, SSH keys 365 days, the other kinds no reminder. A certificate is also due 14 days before it expires. You can change the number of days or clear it to turn the reminder off, in the vault or with orboto credential create|update --rotation-days N|off. When a credential is due, the person who created it and everyone who can manage the project's secrets get one notification, in the app and by email. The first check after the upgrade to 0.211.0 notifies about every credential that is already overdue by its kind's default, so expect a one-time burst. The credential, its [[secret:KEY]] chip and the rotationDue field of orboto credential list show "Rotation due" until it is rotated; orboto credential list --table prints it as a column. Rotating starts the count again. Under Security -> Credentials you can sort by the next rotation date.

Who can see what

Two layers of access apply together:

  • A role permission (secret:view, secret:reveal, secret:manage) decides whether your role may use the credential vault at all. Project-admins and super-admins hold all three for their scope by default; the built-in developer role holds view + reveal.
  • An explicit grant on the specific credential decides which credential you may actually see. Project-admins and super-admins manage every credential in their scope without needing a grant; everyone else needs both the role permission and a grant.

A credential you cannot see answers "not found", not "forbidden" - the vault does not confirm that a secret you have no access to exists.

Referencing a credential

Instead of pasting a value, reference it from a doc or a ticket with [[secret:KEY]]. It renders as a chip carrying the credential's title; a reveal button appears for anyone holding secret:reveal on that credential. The editor's link picker (the same one used for [[doc:...]] and [[ticket:...]] references) offers a credential search tab.

Reveal audit

Every reveal - through the web UI, the CLI or the REST API - is logged: who, which credential, when, and the agent session it came from when one was declared. The credential's own page shows its reveal history; Admin -> Security -> Credentials -> Reveal audit shows it workspace-wide for super-admins. A principal revealing an unusually large number of credentials in 24 hours triggers a cooldown-gated alert to the workspace's secret:manage holders. Each entry names its purpose: a reveal, a one-time code (orboto credential totp, the chip or the credential page), a file download, a browser login, or an SSH agent load. Added in v0.218.0 A fill by the orboto browser extension is a browser login reveal that also shows the page origin it filled; a copy from the extension's side panel is marked as a copy. A load by a lane's runner also names the lane and the job.

Rotating a value

Rotating a credential (web, CLI orboto credential rotate, or REST) replaces the value; the previous one stays readable for the grace window shown on the credential's page, then is available only via that history until the next rotation. Rotating a key pair takes the new private and public key, a certificate its new PEM (the expiry is read again), a file a new upload and a recovery-code list a new list. The replaced file is deleted.

Where the browser extension offers a login

Added in v0.218.0 The orboto browser extension offers a login or a password on a page only when the page matches it. Open the credential and, under Browser extension, choose Offer this login on: every page of the same registrable domain as the URL (the default), pages of exactly the URL's host, or pages of that host under the URL's path. Also offer it on these hosts takes up to 20 more hosts, one per line, which follow the same rule. Both need manage access on the credential. The user still needs reveal access to see the offer. How the extension fills, locks and asks for a second factor is on Browser for your agents; the idle time before it locks is set under Admin -> Security -> Browser extension vault.

Logging in through the orboto browser

Added in v0.211.0 A login credential can be used by the orboto browser without anyone revealing it: orboto browser login --credential KEY types the username, the password and, when the site asks, the current one-time code, and the agent only learns whether it worked. The browser types a login only into the site the credential belongs to; a credential without an address needs --url. Each step is a reveal with the purpose "browser login" and names the browser session. What the browser types is masked in everything the agent sees. The full behavior is on Browser for your agents.

To let a lane's registered browser use a login without a person holding a grant, open the credential, choose Grant access -> Browser session and pick the browser, or run orboto credential grant KEY --browser-session NAME. That grant applies when the login reaches the browser from another machine through orboto. A browser that runs in the lane's own runner uses the agent account instead: orboto credential grant KEY --user AGENT_ACCOUNT --level reveal.

After three failed logins in a row the credential shows Browser login paused. Check the account on the site, then click Reset (needs secret:manage) or run orboto credential reset-login KEY. Anyone who can see the credential also sees how many logins failed. A recovery code is used only when the agent was told to allow it; it is marked used and the person who created the credential gets a notification.

Passkeys

Added in v0.211.0 A passkey is a credential kind of its own. The orboto browser creates it on a site with orboto browser passkey register --credential KEY (needs secret:manage in the project or workspace it lands in) and uses it for orboto browser login --credential KEY. The credential page shows the site, the credential ID, the counter, how the passkey is transported, its algorithm (ES256 or EdDSA) and which browser registered it, all read-only. A passkey is never revealed and never rotated: its private key leaves the vault only into the orboto browser for one login, and every such use appears in the reveal history with the purpose "browser login". To replace one, register a new passkey on the site and delete the old credential.

When a site refuses a passkey, the credential gets a Stale badge and its creator is notified. A login credential can name a passkey in Linked passkey; the browser then answers the site's passkey prompt during that login. To let a lane's registered browser use a passkey, grant it like a login: Grant access -> Browser session or orboto credential grant KEY --browser-session NAME.

Under Security -> Credentials, filter by kind. The passkey view lists every passkey with its site, when it was registered, when it was last used, its counter, whether a site refused it (with the date) and which orboto browser registered it.

Backup and restore

A full-workspace backup carries credential rows as ciphertext, exactly as stored - the backup file itself never contains a plaintext secret. Restoring onto the same instance (same encryption key) decrypts normally. Restoring onto a different instance - a new deployment, a disaster-recovery target with a different key - leaves credential values undecryptable, the same limitation a restored git connection token has. Re-enter credentials by hand after a cross-instance restore, or carry the source instance's encryption key over to the target first.

Reaching it from an agent

The credential vault is reachable over REST and the CLI (orboto credential list|show|reveal|totp|create|update|rotate|use-code|grant|revoke|reset-login|ssh-agent, orboto browser login --credential KEY for a login or passkey used by the orboto browser, and orboto browser passkey register --credential KEY to create a passkey). A secret is never passed on the command line: the CLI reads it from a file (--value-file, --totp-file, --passphrase-file and similar), from STDIN, or asks for it with the input hidden. orboto credential reveal KEY --field password prints one field, and --field file --out PATH saves a file credential; it has no MCP tool of its own (the full MCP manifest had no more room, the same trade-off already made for a couple of other admin surfaces) and no in-app chat mirror (a chat transcript is stored and AI-visible, so a secret must never be typed into one). An MCP-connected agent reaches it through the generic orboto_api_search + orboto_api_call escape hatch.

On this page