Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tesslate.com/llms.txt

Use this file to discover all available pages before exploring further.

Tesslate OpenSail

Overview

OpenSail supports five authentication paths, all enforced by the orchestrator:
  1. Password (bcrypt) with optional email 2FA
  2. OAuth login (GitHub, Google)
  3. OAuth connection for third-party services (deployments, git providers, MCP platform apps)
  4. External API keys (tsk_*) for the external agent API and gateway
  5. Desktop pairing (tsk_* exchanged via deep link and stored in Stronghold)
Sessions use short-lived JWT access tokens with refresh token rotation. CSRF protection is a double-submit cookie pattern. Secrets at rest (OAuth tokens, channel credentials, deployment credentials) are encrypted with Fernet.

Password and email 2FA

1

Sign up

POST /api/auth/register with email, username, password. The orchestrator hashes the password with bcrypt and returns an access token. The first account on a fresh install is promoted to admin.
2

Log in

POST /api/auth/login with username or email and password. Returns an access token (default 30 min) and sets a refresh cookie (default 14 days).
3

Email 2FA (optional)

When TWO_FA_ENABLED=true, login triggers POST /api/auth/2fa/send which emails a 6-digit code. Submit with POST /api/auth/2fa/verify. Codes are Argon2 hashed, limited to 5 attempts, and expire in minutes.
4

Refresh

Access tokens renew automatically via the refresh cookie. No explicit refresh call is needed from the client.
Required environment variables for email flows: SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SENDER_EMAIL, SMTP_USE_TLS.

OAuth login providers

Two providers ship out of the box. Both are optional; absence of credentials disables the provider gracefully.
Scopes: user:email, read:user.
VariablePurpose
GITHUB_CLIENT_IDClient ID from github.com/settings/developers
GITHUB_CLIENT_SECRETClient secret
GITHUB_OAUTH_REDIRECT_URIMust match the OAuth app exactly, including protocol and path
Local dev callback: http://localhost/api/auth/github/callback.
Multiple providers can link to the same account. If the OAuth email matches an existing user, providers attach automatically.

OAuth for deployment targets and git

Connect providers for push-based deployments and private repo import. Configure client IDs and secrets under Settings -> Integrations. Tokens are Fernet-encrypted before being stored in DeploymentCredential.
ProviderEnv variables
VercelVERCEL_CLIENT_ID, VERCEL_CLIENT_SECRET, VERCEL_OAUTH_REDIRECT_URI
NetlifyNETLIFY_CLIENT_ID, NETLIFY_CLIENT_SECRET, NETLIFY_OAUTH_REDIRECT_URI
HerokuHEROKU_CLIENT_ID, HEROKU_CLIENT_SECRET, HEROKU_OAUTH_REDIRECT_URI
DigitalOceanDIGITALOCEAN_CLIENT_ID, DIGITALOCEAN_CLIENT_SECRET, DIGITALOCEAN_OAUTH_REDIRECT_URI
Git providers (GitHub, GitLab, Bitbucket) are optional. Without them you can still import public repos.

External API keys

The external agent API lets machine users invoke agents with a bearer token. Keys are minted from Settings -> API Keys (POST /api/external-api-keys). They are SHA-256 hashed before storage (ExternalAPIKey). Only the newly minted secret is ever shown.
1

Mint a key

Visit Settings -> API Keys and click Create key. Name it, pick scope (project-scoped or team-scoped), and copy the tsk_* secret immediately.
2

Invoke the agent

POST /api/external/agent/invoke with Authorization: Bearer tsk_.... Returns task_id and events_url right away.
3

Stream events

Subscribe to GET /api/external/agent/events/{task_id} (SSE) or poll GET /api/external/agent/status/{task_id}.
4

Revoke

Delete the key in the UI or DELETE /api/external-api-keys/{id}. The hash is wiped; in-flight tasks abort on their next iteration.
See the external agent API guide for scopes, rate limits, and webhook callbacks.

Desktop pairing

The desktop app can pair to a cloud instance (tesslate.com or your own self-hosted cluster). Pairing unlocks the marketplace, bidirectional sync, and the remote K8s runtime.
1

Mint a pairing key

In the cloud instance, Settings -> API Keys -> Create desktop key. This returns a short-lived deep-link URL (opensail://pair?token=...).
2

Open the link

Click the URL on the machine running the desktop app. The Tauri deep-link handler exchanges the token for a long-lived tsk_* key.
3

Store in Stronghold

The key is written to the Tauri Stronghold vault (encrypted on disk with a per-machine passphrase). The sidecar never sees the plaintext key at rest; it is injected into the per-request Authorization header by the Rust shell.
4

Verify

GET /api/desktop/session/ping confirms pairing. The tray icon turns green.
Every cloud call made by the desktop app uses this key. Unpair by deleting it in the desktop Settings page, which also revokes it on the cloud side.

Session management

Access tokens default to 30 minutes (ACCESS_TOKEN_EXPIRE_MINUTES). Refresh tokens default to 14 days (REFRESH_TOKEN_EXPIRE_DAYS). Refresh is automatic while the browser tab is open.
Production sets COOKIE_SECURE=true, COOKIE_SAMESITE=lax, and COOKIE_DOMAIN=.your-apex.tld so subdomain project URLs authenticate cleanly.
POST /api/auth/logout clears the refresh cookie and invalidates the server-side refresh record.

Security model

ConcernMechanism
Password storagebcrypt with per-user salt
2FA codesArgon2 hashed, 5-attempt cap, minute TTL
Refresh rotationOne-time-use refresh tokens, detected reuse invalidates the session
Secrets at restFernet (CHANNEL_ENCRYPTION_KEY, DEPLOYMENT_ENCRYPTION_KEY, falls back to a key derived from SECRET_KEY)
CSRFDouble-submit cookie pattern with X-CSRF-Token header
OAuth stateSigned, single-use state token; mismatches abort the flow
API keystsk_* prefix, SHA-256 hashed in DB, shown once at creation
Desktop pairingShort-lived deep-link token; long-lived key lives in Stronghold
Rotating SECRET_KEY invalidates existing JWTs and may make Fernet-encrypted data unrecoverable unless you also rotate CHANNEL_ENCRYPTION_KEY and DEPLOYMENT_ENCRYPTION_KEY in sync.

Troubleshooting

Click Forgot Password on login, enter your email, and follow the link. Resets require a working SMTP configuration.
Check the caps-lock state, try username instead of email, and check the email inbox (and spam) for a 2FA code if TWO_FA_ENABLED=true. Browser cache flushes often resolve stale cookies.
The OAuth app’s callback URL must exactly match *_OAUTH_REDIRECT_URI, including protocol (http vs https) and path.
Just log in again. Data is safe; sessions can expire after extended inactivity. Check that COOKIE_DOMAIN covers the subdomain you are on in production.
Verify the deep-link scheme (opensail://) is registered on the OS. On Windows, reinstall with admin rights if the registration is missing. The token expires in minutes; mint a fresh one.

Next steps

API keys guide

Mint keys, invoke the external agent API, set scopes.

Desktop install

Install the desktop app and pair it to your cloud.

Configuration

OAuth, SMTP, and cookie settings for self-hosted.

Security model

Full security overview in the architecture page.