API reference
The backend exposes six Connect-RPC services under the opensecret.v1 package. Connect speaks HTTP, so a client is a plain fetch away; the frontends use the generated TypeScript client in open-secret-shared.
Identity always comes from the session token, never from a request-body user id, so authenticated calls can only touch the caller's own data. Only the five endpoints marked public work without a token.
AuthService
| RPC | Purpose | Public |
|---|---|---|
Signup | Create the first account/device; returns a session token. | ✅ |
Challenge | Begin login: submit an auth public key, get a one-time challenge. | ✅ |
Verify | Complete login: submit the signed challenge, get a session token. | ✅ |
Logout | Invalidate the current session client-side. | - |
InstanceService
| RPC | Purpose | Public |
|---|---|---|
GetConfig | Non-secret instance config: signups enabled, backup enabled + the backup public key, the deployment auth_mode, and (in IDP mode) the public OIDC client config. Never a secret or the K2 unlock half. | ✅ |
IdpService
Present in IDP-mode deployments (rejected with FailedPrecondition in standalone).
| RPC | Purpose | Public |
|---|---|---|
ExchangeSSOToken | Validate an OIDC ID token (issuer/audience/expiry/signature via JWKS), JIT-provision the user + identity device on first login, and mint a session token. | ✅ |
GetWrappingFactorK2 | Return the server-held K2 half of the vault key + the escrowed wrapped vault blob. Insufficient to decrypt without the client-only factor1. | - |
PutEscrowedVault | Store the wrapped identity-keypair blob so a second device can recover it after SSO. | - |
UserService
| RPC | Purpose |
|---|---|
Me | The caller's profile (id, email, display name, admin flag, backup opt-in). |
LookupByEmail | Resolve an email to a user id (for sharing). |
SetBackupOptIn | Opt the user's entries into operator backup (signed command). |
DeviceService
| RPC | Purpose |
|---|---|
AttachDevice | Register a new device's public keys with a parent attestation. |
List | List the caller's devices. |
Rename | Rename a device (signed command). |
Revoke | Revoke a device (signed command). |
Delete | Delete a device (signed command). |
ListPubkeysForUser | Fetch a user's device encryption public keys (to seal shares when sharing). |
EntryService
| RPC | Purpose |
|---|---|
Create | Create an entry's first version with per-device shares. |
Update | Publish a new version (optimistic-concurrency checked). |
Get | Fetch a version (default: current) and the caller's share. |
List | List readable entries. |
ListEntryVersions | Return version-history metadata for an entry, newest first. Content is fetched per-version via Get. |
Delete | Delete an entry (signed command). |
AddRecipient | Share with another user by sealing shares to their devices. |
RemoveRecipient | Stop sealing future versions to a user. |
BackfillDevice | Seal existing entries to a newly-paired device. |
SetBackupEnabled / SetBackupEnabledBulk | Toggle operator-backup sealing per entry. |
AttachBackupShare | Attach the backup-key-sealed ciphertext for a version. |
Sensitive mutations (revoke, delete, rename, share, backup toggles) carry a command signature from the caller's device in addition to the session token, and the server re-verifies device trust on them, so a stolen-but-revoked token can't issue destructive commands.
Authorization invariants
Three rules the server enforces and clients rely on:
- Owner scoping, every entry/device query is constrained by the token-derived user id, with belt-and-braces owner clauses in SQL.
- Share coverage, a write must seal a share for every active device of every recipient; the server rejects writes that don't.
NotFoundvsPermissionDenied, chosen deliberately and load-bearing for clients; the distinction isn't collapsed.