Authentication
OpenTusk uses three layers of authentication depending on what you’re doing: accounts for identity, API keys for programmatic access, and Sui private keys for on-chain encryption operations. Invite codes tie all three together for agent onboarding.
Accounts
Section titled “Accounts”Creating an account
Section titled “Creating an account”Sign up at app.opentusk.ai or from the CLI. All new signups require an access code during the early access period.
Go to app.opentusk.ai/signup. You can pass an access code via URL parameter:
https://app.opentusk.ai/signup?code=YOURCODEYou can also sign up with Google via Sui zkLogin — the same access code requirement applies.
opentusk setup# Choose "Create a new account"# Prompts for email, password, access code, display name# Automatically generates an API key and optionally sets up Sui wallet + vaultLogging in
Section titled “Logging in”Sign in at app.opentusk.ai with email/password or Google OAuth. The web app uses short-lived JWT access tokens (15 min) with refresh tokens (30 days) — this is handled automatically.
Refresh tokens use family-based reuse detection: if a token is reused after rotation, the entire token family is revoked to prevent session hijacking.
# Interactive (prompts for email + password)opentusk login
# With an API keyopentusk login --api-key otk_your_key
# With email and password
# With an invite code (for agents — see below)opentusk login --invite-code otinv_abc123...How web sessions work
Section titled “How web sessions work”The web app authenticates via custom JWTs:
| Token | Lifetime | Transport |
|---|---|---|
| Access token | 15 minutes | Authorization: Bearer header (HS256 JWT) |
| Refresh token | 30 days | POST /api/auth/refresh to rotate |
When the access token expires, the web app silently refreshes it using the refresh token. If the refresh token is expired or revoked, the user is logged out.
Google sign-in & Sui zkLogin
Section titled “Google sign-in & Sui zkLogin”The web app supports two Google-based flows, both starting from GET /api/auth/google or GET /api/auth/google/zklogin to obtain a Google OAuth authorization URL.
Plain Google OAuth — POST /api/auth/google/callback
Used for sign-up and sign-in when the user doesn’t want an on-chain identity yet. The server exchanges the authorization code for ID and access tokens, finds or creates a user record, and issues OpenTusk JWTs. No Sui keypair is derived.
Google + Sui zkLogin — POST /api/auth/google/zklogin-auth (combined endpoint), or the two-step pair POST /google/zklogin-token → POST /google/zklogin-auth.
zkLogin derives a Sui address from a Google account using zero-knowledge proofs, so signing in with Google also unlocks shared-vault access without a separately-managed wallet. The user’s suiAddressSource is set to 'zklogin' (vs 'external' for addresses linked via link-sui).
The ZK prover used depends on the network:
| Network | Prover |
|---|---|
Mainnet (OPENTUSK_ENV=production) | Enoki — VITE_ENOKI_API_KEY required, baked into the web bundle |
Testnet (OPENTUSK_ENV=development) | prover-dev.mystenlabs.com (Mysten’s dev prover) |
A network/prover mismatch will silently produce ZK proofs the on-chain verifier rejects — see Access Control Contract for the deployed verifier.
Password reset & email verification
Section titled “Password reset & email verification”Both flows are token-based — the server emails a single-use token that must be POSTed back within its expiry window.
| Endpoint | Purpose |
|---|---|
POST /api/auth/forgot-password | Request a password-reset email. Body: { email }. Always returns 200 (does not reveal account existence). Rate-limited per IP. |
POST /api/auth/reset-password | Complete a password reset. Body: { token, newPassword }. Token is from the email link. |
POST /api/auth/verify-email | Mark an email as verified. Body: { token }. Token is from the verification email sent at signup. |
POST /api/auth/resend-verification | Re-send the verification email. Body: { email }. Rate-limited per IP. |
POST /api/auth/check-access-code | Validate an access code client-side before submitting the signup form. Body: { code }. |
Email verification is not required to access most of the API — uploads, downloads, and vault operations work for unverified accounts. Verification is required for billing operations (Stripe checkout, plan changes) so we can recover account access if a credit card is misused.
API keys
Section titled “API keys”API keys are the primary way to authenticate with the OpenTusk API from the CLI, SDK, scripts, CI pipelines, and MCP servers. Every API call (outside of web sessions) uses an API key.
How they work
Section titled “How they work”- You create a key — the full key (
otk_...) is shown once - The server stores only a
SHA-256hash of the key - On each request, the server hashes the incoming key and looks it up by hash
- The raw key cannot be retrieved later — if you lose it, create a new one
Using a key
Section titled “Using a key”Pass the key in the Authorization header on every request:
curl https://api.opentusk.ai/api/files \ -H "Authorization: Bearer otk_your_key"Or in the SDK:
import { OpenTuskClient } from '@opentusk/sdk';const opentusk = new OpenTuskClient({ apiKey: 'otk_your_key' });Or set it as an environment variable for the CLI:
export OPENTUSK_API_KEY=otk_your_keyopentusk lsCreating keys
Section titled “Creating keys”Go to Settings > API Keys > Create API Key at app.opentusk.ai.
opentusk account api-keys create "My Key"
# With scopes and vault restrictionsopentusk account api-keys create "Scoped Key" \ --scopes files:read,files:write \ --vaults vault-id-1,vault-id-2 \ --expires 30curl -X POST https://api.opentusk.ai/api/auth/api-keys \ -H "Authorization: Bearer otk_your_key" \ -H "Content-Type: application/json" \ -d '{"name": "CI Pipeline"}'Key properties
Section titled “Key properties”| Property | Description |
|---|---|
| Prefix | All keys start with otk_ |
| Scopes | Optional — restrict to specific operations (e.g., files:read) |
| Vault restrictions | Optional — limit access to specific vault IDs |
| Expiry | Optional — auto-expire after N days |
| Sui address | Optional — binds a Sui address for shared vault encryption |
| Revocation | Immediate — sets revoked_at, all subsequent requests fail |
Revoking keys
Section titled “Revoking keys”opentusk account api-keys list # Find the key IDopentusk account api-keys revoke <key-id>Revocation is instant. Any in-flight requests using the key will fail.
Sui private keys
Section titled “Sui private keys”Sui private keys are used for on-chain operations — specifically, encrypting and decrypting files in shared vaults using the SEAL protocol. They are separate from API keys and serve a different purpose.
Why a Sui key is needed
Section titled “Why a Sui key is needed”Shared vaults use SEAL (Sui Encryption with Access Lists) — an identity-based encryption scheme where access is controlled by Sui addresses on the blockchain. To encrypt or decrypt files in a shared vault, you need a Sui Ed25519 private key that corresponds to an address listed on the vault’s on-chain allowlist.
| Operation | Requires API key | Requires Sui key |
|---|---|---|
| Upload to public vault | Yes | No |
| Upload to shared vault | Yes | Yes (SEAL encrypt) |
| Download from public vault | Yes | No |
| Download from shared vault | Yes | Yes (SEAL decrypt) |
| Create/manage vaults | Yes | Yes (signs on-chain tx) |
| Grant/revoke vault access | Yes | Yes (signs on-chain tx) |
| List files, account info, etc. | Yes | No |
Setting up a Sui key
Section titled “Setting up a Sui key”opentusk setup# The wizard offers to generate a Sui keypair and link it to your account# Store a key in CLI configopentusk account setup-sui suiprivkey1...
# Link the address to your account (one-time)opentusk account link-sui 0x1234...abcd
# Verifyopentusk account show-sui# For scripts, CI, and MCP serversexport OPENTUSK_SUI_PRIVATE_KEY=suiprivkey1...How Sui keys are stored
Section titled “How Sui keys are stored”| Context | Storage | Notes |
|---|---|---|
| CLI config | ~/.config/opentusk/config.json | Stored by opentusk setup or account setup-sui |
| Environment variable | OPENTUSK_SUI_PRIVATE_KEY | Takes precedence over config |
| MCP server config | Embedded in .mcp.json / agent config | Written by opentusk mcp install-config |
| Web app | Browser wallet (Sui Wallet, Phantom) | Never touches the server |
The Sui private key never leaves your machine. The server only knows your Sui address (public), not the private key.
Linking your Sui address
Section titled “Linking your Sui address”After setting up a Sui key, you need to link its public address to your OpenTusk account. This tells the server which Sui address is yours, so it can include you on vault allowlists.
opentusk account link-sui 0x1234...abcdThe opentusk setup wizard does this automatically when generating a keypair.
Invite codes (agent onboarding)
Section titled “Invite codes (agent onboarding)”Invite codes combine all three authentication layers into a single onboarding command for AI agents. One code sets up an API key, a Sui keypair, and the address link — all scoped to the owner’s account.
How it works
Section titled “How it works”Owner Agent │ │ ├─ opentusk invite create │ │ → gets otinv_abc123... │ │ │ ├─ shares code with agent ────────►│ │ ├─ opentusk login --invite-code otinv_... │ │ ├─ Generates Sui Ed25519 keypair │ │ ├─ Redeems code → receives API key │ │ └─ Stores API key + Sui key in config │ │ ├─ opentusk vault members add │ │ <vault> <agent-sui-address> │ │ ├─ Can now encrypt/decrypt in shared vaultCreating codes
Section titled “Creating codes”# Basic — 1 hour expiryopentusk invite create --name "my-agent"
# With scope and vault restrictionsopentusk invite create --name "reader" \ --scopes files:read \ --vaults vault-id-1,vault-id-2 \ --expires 24
# Manage codesopentusk invite listopentusk invite revoke <invite-code-id>Codes are single-use and short-lived (default 1 hour, max 72 hours). Like API keys, the full code is shown once — the server stores a SHA-256 hash.
Owner vs agent access
Section titled “Owner vs agent access”When an agent redeems an invite code, its API key gets a Sui address that differs from the account owner’s. This automatically classifies it as an agent key with restricted permissions.
| Owner | Agent | |
|---|---|---|
| Upload / download / list files | Yes | Yes |
| Create / list / manage folders | Yes | Yes |
| List vaults and vault details | Yes | Yes |
| Soft-delete files (trash) | Yes | Yes |
| Create / update / delete vaults | Yes | No |
| Create / revoke API keys | Yes | No |
| Add / remove vault members | Yes | No |
| Billing and plan changes | Yes | No |
| Manage webhooks | Yes | No |
The GET /api/account endpoint returns accessLevel: "owner" or "agent" so clients can adapt their behavior.
See Invite Codes for the full guide — Sui key locking, managing multiple agents, and revoking access.
Error responses
Section titled “Error responses”Authentication failures return 401 Unauthorized:
{ "error": "Unauthorized"}This applies to missing keys, invalid keys, revoked keys, and expired JWTs.