Skip to content

Authentication

Every authenticated request carries the same header:

Authorization: Bearer <credential>

The API accepts two kinds of credential on it and tells them apart by shape.

Credential Who uses it Looks like
Firebase ID token the dashboard, in a browser a JWT (three base64 segments)
CAVS API token CLI, SDKs, CI, scripts, git-lfs starts with cavs_

Identity for people is Firebase Authentication. The dashboard signs the user in, obtains an ID token, and sends it on every API call. The API verifies the RS256 signature against Google’s public JWKS and checks the issuer and audience against its configured Firebase project.

On first sign-in the API creates the corresponding user record; subsequent sign-ins are a single indexed read.

What the dashboard sends
GET /api/v1/users/me HTTP/1.1
Host: cavsnode.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm…
Sync the user record explicitly (idempotent)
curl -sS -X POST https://cavsnode.com/api/v1/auth/session \
-H "Authorization: Bearer $FIREBASE_ID_TOKEN"

Tokens are created per organization in the dashboard (Settings → Tokens) or via POST /organizations/{org}/tokens. Three families, distinguished by prefix:

Prefix Kind Acts as Typical use
cavs_pat_ Personal access token you your laptop, the CLI
cavs_repo_ Repository token itself, bound to one repository one deploy target
cavs_ci_ CI/machine token itself pipelines

Only the SHA-256 hash is stored. The secret is displayed once at creation and cannot be recovered. Every successful use updates the token’s last-seen timestamp and IP, which you can review in the token list.

Terminal window
curl -sS https://cavsnode.com/api/v1/users/me \
-H "Authorization: Bearer cavs_pat_XXXXXXXXXXXXXXXX"

Full lifecycle, scope selection and rotation: API tokens.

Authentication establishes who. Authorization then composes five inputs, and any one of them can deny:

organization role from your membership
+ repository override per-repository access level, if any
+ token scope a hard cap — never widens anything
+ account status must be ACTIVE
+ organization status must be active

For a personal access token the effective permission is your role ∩ the token's scope. Losing a role immediately narrows every PAT you hold. For repo and CI tokens there is no user, so the scope alone decides.

See Roles & permissions for the matrix.

cav login validates the token against the API, then writes it to:

~/.config/cav/config.toml
api_base = "https://cavsnode.com"
token = "cavs_pat_…"
account = "[email protected]"

Prefer the interactive prompt or --token-stdin so the secret stays out of your shell history:

Terminal window
cav login # prompts
echo "$CAVS_TOKEN" | cav login --token-stdin

Precedence for the API base URL: --api flag → $CAVS_API → the stored config → the built-in default.

The data plane hands you short-lived signed object-storage URLs. Anyone holding one can perform that single operation until it expires (15 minutes by default).

  • Never log them, never put them in error reports or CI output.
  • Don’t persist them; re-request instead — authorization is cheap.
  • They carry no identity, so they don’t appear in the audit log. The authorization call that produced them does.
Condition Response
No Authorization header 401 unauthorized — “missing bearer token”
Malformed or expired Firebase token 401 unauthorized — “invalid token”
Unknown, expired or revoked API token 401 unauthorized — “invalid or expired token”
User status is not ACTIVE 403 forbidden — “account is suspended”
Role, override or scope insufficient 403 forbidden

Three routes need no credential:

Endpoint Purpose
GET /healthz Liveness. Internal only — an external request hits the load balancer’s own 404.
GET /readyz Readiness, including a database ping. Use this for external checks.
GET /api/v1/plans The public plan catalogue.
POST /api/v1/billing/webhook The billing provider’s callback, verified by signature rather than a bearer token.