Quickstart
From a running server to your first authenticated call.
1. Run the server
The server needs Redis (with the JSON and Search modules), an RSA keypair for signing tokens, and a public base URL:
export REDIS_URL=redis://localhost:6379
export PUBLIC_API_URL=http://localhost:4000
# Generate a signing keypair once and keep jwt_private.pem secret
snug jwt keygen --out-dir .
export JWT_PRIVATE_KEY="$(cat jwt_private.pem)"
export JWT_PUBLIC_KEY="$(cat jwt_public.pem)"
cargo run --bin snug-api --features fullThe server listens on port 4000 by default. Verify it is up:
curl http://localhost:4000/health2. Create an account
Signing up does two things: it creates your user, and it registers your
personal tenant. Tenancy is a closed world - every token names a tenant in
its azp claim, and a token for an unregistered tenant is rejected with
401 tenant is not registered before any handler runs. Signup, organization
creation, and directory provisioning are what register tenants.
snug auth signup --email you@example.com --password '...' --username you{
"email": "you@example.com",
"email_verified": false,
"status": "account created. logged in"
}The CLI stores your session in ~/.snug/credentials.json and refreshes it
automatically. Your user ID doubles as your personal tenant ID:
snug --output json auth me{
"id": "q7Fk2PmXbTn4RsWvZc8L",
"email": "you@example.com",
"email_verified": false,
"role": "user"
}One gate remains: service APIs require a verified email, and an unverified
session gets 403 EMAIL_NOT_VERIFIED. On a deployment with real email
configured, click the link in the verification message - though note that a
real provider may reject placeholder recipients like example.com outright,
in which case no message is sent at all. On a local server with the default
log email provider, nothing is delivered either. In both cases, skip the
session and mint tokens directly, which is the natural local-dev path
anyway:
3. Or mint tokens directly (local development)
When you run the server yourself you hold its signing key, so you can mint
tokens for any subject without the signup ceremony - as long as the token's
tenant is registered. Read your personal tenant ID from auth me and mint against it:
TENANT=$(snug --output json auth me | jq -r .id)
export SNUG_API_TOKEN=$(snug jwt generate user -u dev -t "$TENANT" --format compact)Tokens minted this way have full access by default; add
--scope 'kv:read ...' to restrict them. See
Authentication for the whole model.
4. First calls
Store and read a value in the KV service, through the CLI:
snug kv set docs/hello '{"greeting": "hello world"}'
snug --output json kv get docs/hello{
"key": "docs/hello",
"value": { "greeting": "hello world" },
"content_type": "application/json",
"updated_at": "2026-08-28T03:41:02.485104Z"
}The same operation over raw HTTP - service endpoints live under /api/v1,
and every response is wrapped in the standard envelope:
curl -H "Authorization: Bearer $SNUG_API_TOKEN" \
http://localhost:4000/api/v1/kv/docs/hello{
"status": 200,
"msg": "OK",
"data": {
"key": "docs/hello",
"value": { "greeting": "hello world" },
"content_type": "application/json",
"updated_at": "2026-08-28T03:41:02.485104Z"
}
}The CLI unwraps that envelope for you; over HTTP you read data. Errors use
the same shape with an error code and no data - the details are in
Responses and errors.
Where to go next
- Authentication - credentials, scopes, tenants, and roles
- Responses and errors - the envelope contract
- Rate limits - budgets, headers, and tiers
- Search queries - filtering and pagination on list endpoints
- CLI - the full command-line contract
- API reference - every endpoint, callable