Skip to content

Authentication

  • stdio is trusted-local. The operator runs the binary against their own vault, so stdio calls are authenticated with full local scope and need no token.
  • HTTP is untrusted by default. When the HTTP transport is enabled you set auth.mode: jwt, and every request must carry a valid signed bearer token.

Clients authenticate with a signed JWT bearer token. Each token carries:

  • the vault it may act on,
  • a scope set (family:resource, e.g. read:notes, write:notes),
  • an expiry (TTL).

Folder-path restrictions are applied separately by the folder ACL (glob allow/deny on paths), not encoded in the scope string. The signing key lives outside the vault and is never logged. Tokens are verified with a pinned algorithm — alg: none and unsigned tokens are rejected — and checked for signature and expiry on every request.

Asymmetric JWT verification (RS256 / ES256 / EdDSA)

Section titled “Asymmetric JWT verification (RS256 / ES256 / EdDSA)”

Beyond the shared-secret HS256 path above, auth.mode: jwt can verify tokens signed with an asymmetric key, so the server holds only a public key while the issuer keeps the private key. Provide a JWKS in place of (or alongside) jwtSecret:

  • auth.jwks — an inline JWKS document ({ "keys": [ … ] }).
  • auth.jwksFile — a path to a JWKS document, loaded once at transport boot. File or inline only — there is no URL fetch, so no new network attack surface.
  • auth.algorithms — an allowlist of asymmetric algorithms. Defaults to ["RS256", "ES256", "EdDSA"] when omitted.
{
"auth": {
"mode": "jwt",
"jwksFile": "/etc/obsidian-tc/jwks.json",
"algorithms": ["RS256", "EdDSA"]
}
}

Key rotation is kid-based: publish the old and new keys together in the JWKS set and the token’s kid header selects the verifying key (handled by jose).

Algorithm-confusion is structurally impossible. An HS256 token is verified only against jwtSecret; an asymmetric token is verified only against the JWKS — a public key can never be presented as an HMAC secret. HS256-only deployments are unchanged; asymmetric verification is purely additive and opt-in.

The HTTP transport and the optional /metrics endpoint bind to loopback unless explicitly configured otherwise. Binding either to a non-loopback interface requires JWT auth; a non-loopback bind with auth.mode: none is refused at startup rather than silently exposing an open surface. The HTTP edge also validates the Origin header (rejecting DNS-rebinding / cross-origin browser requests with 403).

OAuth resource-server discovery (optional)

Section titled “OAuth resource-server discovery (optional)”

For clients that expect OAuth-style discovery, obsidian-tc can act as an OAuth 2.0 resource server (RFC 9728) without changing its HS256 token format. When you set auth.resource (this server’s canonical URI) plus one or more auth.authorizationServers, the HTTP transport serves a Protected Resource Metadata document at /.well-known/oauth-protected-resource (and the path-inserted …/mcp) and returns WWW-Authenticate: Bearer resource_metadata="…" on a 401, so a spec-compliant client can discover the authorization server. This is opt-in and off by default; there is no in-repo authorization server (token issuance, Dynamic Client Registration, OIDC discovery) — point authorizationServers at your external AS.

See also Scopes & Folder ACLs and HITL Elicitation.