Credentials & Authentication
This page covers the two halves of authentication in Anyy:
- Outbound — the credentials Anyy uses to reach a configured model provider (an API key, or a provider account "sign-in" flow), and where those secrets are kept on disk.
- Inbound — who is allowed to reach the resident gateway and the on-demand dashboard.
Providers are configurable backends. Anyy identifies as Anyy; a provider's name (and its sign-in flow, where it has one) is a backend label and an authentication method — never Anyy's identity. For choosing which model to run, see Configuring Models. For the broader trust model and how secrets are redacted, see Security & Privacy. To set credentials up the first time, the Setup wizard walks you through it.
Provider API Keys
The default way to authenticate to a provider is an API key. You enter it through the setup wizard:
anyy setup model
The wizard asks for the provider, the authentication method, and the default
model. For an API-key provider it stores the key as a profile-local secret
file and writes only a reference to it into your configuration — the key value
itself never lands in config.yaml.
References use a small scheme prefix so configuration can point at a secret without inlining it:
| Scheme | Meaning |
|---|---|
secret:<path-under-secrets> | A file under the profile's secrets/ directory, addressed by its relative path — a bare name like secret:openai-key or a nested path like secret:telegram/personal/token (what the wizard writes for API keys) |
env:<NAME> | An environment variable resolved at use time |
file:<path> | A file you manage yourself |
literal:<value> | An inline value (avoid for real secrets) |
Re-running anyy setup model on a provider that already has a key prints the
key masked and offers to keep, replace, or clear it:
<provider> API key: sk-...1234... OK
[K]eep / [R]eplace / [C]lear (default K):
Keep (or Enter) leaves the existing reference untouched. Replace prompts for a
new key. Clear schedules the secret for deletion — and, because clearing a
credential is a security-relevant change, it is shown in the Review Changes
summary and only applied after you confirm.
Never paste an API key directly into a chat prompt, a tool argument, or a shell
command. Only values Anyy holds as registered secret references are
covered by redaction; a key pasted inline can leak into logs, the audit trail, or
provider error text. Always go through anyy setup model (or a secret: /
env: / file: reference) so the value is stored, not inlined.
Claude OAuth
One Anthropic-family provider backend can authenticate either with a normal API key or by signing in to a Claude account (Claude Pro/Max) and reusing that account's OAuth credentials. This is purely one provider's authentication method: signing in obtains credentials for that backend. It does not make Anyy into Claude or Anthropic, and Anyy never injects a Claude identity into the assistant.
When you pick this provider in anyy setup model, you choose between:
1. Claude Pro/Max subscription (OAuth login)
2. Anthropic API key (pay-per-token)
OAuth login (sign-in flow). Choosing the subscription path runs the provider's own setup-token command in the foreground:
claude setup-token
The provider's CLI owns the browser, the URL, and the code-paste interaction; Anyy does not parse or replay that output. After it returns, Anyy looks for a usable credential in this order:
- Anyy's own stored OAuth credential (
oauth:claude-code), if valid or refreshable. - The provider account's existing credential store (on macOS, the Keychain item
Claude Code-credentials; otherwise~/.claude/.credentials.json), read read-only. - A setup token supplied via a supported environment variable, or pasted manually
when prompted (
sk-ant-oat-...).
If the provider CLI is not installed, Anyy prints install / run / re-run guidance and still lets you paste an existing setup token:
1. Install Claude Code: npm install -g @anthropic-ai/claude-code
2. Run: claude setup-token
3. Follow the browser prompts to authorize
4. Re-run: anyy setup model
A few important properties of this backend's sign-in flow:
- Anyy does not mutate the provider account's own credential files by
default. The Keychain item and
~/.claude/.credentials.jsonare treated as read-only sources. When a token is refreshed, the rotated credential is written to Anyy's own OAuth secret store, not back to the provider's files. - A static setup token never shadows a refreshable sign-in credential. A refreshable credential store is preferred so tokens can renew automatically.
- It is scoped to this one backend. Anthropic-compatible providers (custom proxies and third-party endpoints) never read this sign-in state and never receive its identity headers — they authenticate with their own API key.
A signed-in OAuth credential is a live access/refresh token, not a disposable key.
Logging out of Anyy removes only Anyy's stored copy
(oauth:claude-code); it does not revoke the provider account session or touch
the provider's own credential files. To fully cut access, also revoke the session
in the provider account itself.
Where Secrets Are Stored
All credentials live as files under your Anyy home directory, alongside
the rest of your local-first state. Anyy keeps its state under a home
directory — ~/.anyy for a normal-user install, /root/.anyy when
running as root. Override it with the --home flag or the ANYY_HOME
environment variable (/root/.anyy is the built-in fallback used when no
home is otherwise resolved). The configuration file is <home>/config.yaml (for
example ~/.anyy/config.yaml).
Secrets sit under the secrets/ subtree:
~/.anyy/
├── config.yaml # references secrets, never raw values
└── secrets/
├── <api-key-name> # profile-local API-key files (secret:<name>)
└── oauth/
├── claude-code/ # provider sign-in (OAuth) credential
│ ├── credential.json # access token + refresh token + metadata
│ ├── access_token
│ ├── refresh_token
│ ├── metadata.json # account/expiry/last_refresh (no token values)
│ └── pending.json # in-flight device-login state (transient)
└── openai-codex/ # another provider's sign-in credential
Storage rules enforced by the runtime:
- Restrictive permissions. The
secrets/directory and each OAuth credential directory are created0700; secret files are written0600(owner read/write only). - Atomic writes. OAuth credential files are written to a temp file and renamed into place, so a crash mid-write cannot leave a half-written credential.
- Path-confined references. A
secret:reference is resolved relative tosecrets/; references that try to escape the directory (absolute paths,..) are rejected. - References, not inlines, everywhere else. Configuration, tool definitions, and provider definitions point at a secret by reference; the value is read only at the moment it is needed. The secret-listing API returns reference names and metadata only — never the value.
The secrets/ tree is the most sensitive part of your home directory: it holds
raw API keys and live OAuth tokens. Restrict the whole home directory to your user
account, exclude it from world-readable backups, and never commit it to version
control. The file permissions above protect against other local users, not against
a backup or sync tool that copies the files elsewhere.
Gateway & Dashboard Access Control
Outbound credentials decide what Anyy can reach; inbound access control decides who can reach Anyy.
Gateway listener
Anyy runs as a resident gateway that local clients connect to. Its
local transport is a Unix-domain socket (<home>/anyy.sock, for example
~/.anyy/anyy.sock). The same config also has a TCP/WebSocket listener.
In the current build, omitting gateway.tcp means TCP defaults to enabled on
0.0.0.0:8765 in open mode. Check and harden this explicitly before you run on
a shared machine or LAN.
To keep the gateway local-only, disable TCP:
gateway:
tcp:
enabled: false
If you need TCP, bind it deliberately and choose an access mode:
gateway:
unix_socket: ~/.anyy/anyy.sock
tcp:
enabled: true
listen: "127.0.0.1:8800"
mode: token
The mode controls who may connect:
| Mode | Behavior |
|---|---|
open | No client authentication — avoid except for tightly controlled loopback use |
token | Clients must present a shared token |
pairing | Clients establish access through a pairing exchange; pairing tokens are themselves treated as secrets |
allowlist | Only explicitly allowed clients may connect |
Dashboard
The web dashboard is an on-demand sidecar, not an always-on server. You start it explicitly:
anyy dashboard # local, opens in your browser
anyy dashboard lan # also prints URLs for trusted devices on your LAN
anyy dashboard --status # check whether a sidecar is running
anyy dashboard --stop # stop it
Access works by one-time claim token, exchanged for a session cookie:
- The sidecar issues a single-use claim token and embeds it in the URL it prints (or opens). In LAN mode each printed URL carries its own token.
- The browser posts that token to the dashboard's claim endpoint, which exchanges
it for an HttpOnly session cookie (
anyy_dashboard_session,SameSite=Lax). - Subsequent requests are authorized by that cookie; unauthenticated requests are bounced back to the claim step. The dashboard also restricts which gateway RPC methods it will proxy.
anyy dashboard lan widens the trust boundary to every device that can reach
the printed URL. Share LAN URLs only with devices you trust, treat the claim token
in the URL like a password (it grants a session), and stop the sidecar
(anyy dashboard --stop) when you are done. Never expose the dashboard or a
TCP gateway listener to an untrusted network without a token or pairing and an
allowlist.
Rotating & Revoking Credentials
Rotate or replace a provider API key:
anyy setup model
# choose the provider, then [R]eplace at the key prompt
Replace writes the new key to the same secret reference; the old value is
overwritten in place. Clear (the [C] option) schedules deletion of the secret,
shown in the Review Changes summary and applied only after you confirm.
Sign out of the provider OAuth backend: logging out removes Anyy's stored
oauth:claude-code credential. As noted above, this does not revoke the
provider account session or modify the provider's own credential files — revoke the
session in the provider account if you need to fully cut access.
Refresh happens automatically. For sign-in backends with a refresh token, Anyy refreshes an expiring access token at runtime (the rotated token is written to Anyy's own OAuth store). You do not normally rotate OAuth tokens by hand.
Rotate gateway/dashboard access:
- Restart the dashboard sidecar (
--stopthen start again) to invalidate previously printed claim-token URLs and issue fresh ones. - Change or disable
gateway.tcpsettings inconfig.yamland restart the gateway (anyy gateway restart) to change or close the network listener.
After replacing or clearing a credential, check that no stale copy lingers in an
environment variable (env: reference) or an external file (file: reference)
that configuration still points at — Anyy rotates the value it owns, not
copies you manage elsewhere.
Troubleshooting Authentication
A provider reports "not authenticated" / "needs login."
Run anyy setup model and confirm a credential is present. For an API-key
provider, verify the secret: / env: / file: reference resolves (the file
exists, or the environment variable is set in the gateway's environment). Then
check overall health:
anyy doctor
The OAuth sign-in succeeded in the browser but Anyy still wants a login.
Re-run anyy setup model and choose the subscription path again. Anyy
re-reads the provider account credential store after the setup-token command
returns; if the provider CLI is not installed, follow the printed install / run /
re-run steps, or paste the displayed setup token (sk-ant-oat-...) when prompted.
A token expired. For sign-in backends with a refresh token, this self-heals on the next call. If a credential has no refresh token (for example, a static setup token), re-run the sign-in flow to obtain a fresh one.
A billing or entitlement message appears (not a login error). A provider message about extra usage or quota is a billing/entitlement problem, not an authentication failure. Resolve it in the provider account; re-authenticating will not change it.
The dashboard rejects you or loops back to the claim screen. The claim token is single-use and the session cookie expires. Open the current URL the running sidecar printed (old URLs are stale), or restart the sidecar to get a fresh token:
anyy dashboard --stop
anyy dashboard
A network client cannot reach the gateway.
Confirm gateway.tcp.enabled is not false, the listener address is correct, and
the client satisfies the configured mode (presents the token, completes pairing,
or is on the allowlist). Restart with anyy gateway restart after config
changes.
If you suspect a credential leaked, treat replacement as urgent: replace the provider key (and revoke it in the provider account), restart the dashboard sidecar to invalidate outstanding claim URLs, and review the audit trail for unexpected activity. See Security & Privacy for the redaction and audit guarantees.