Agent skills
This is the agent-facing manual for GAUTH. It is written to be copied whole into an agent prompt.
The live, version-stamped copy is generated by the running service at /skills.md. It fills in this deployment's exact base URLs, token lifetimes and password minimum. Use this page when you need a stable copy.
Connection facts
| what | value |
|---|---|
| public base URL | https://auth.gumpworks.com |
| API base | https://auth.gumpworks.com/api/v1 |
| management API | https://auth.gumpworks.com/api/v1/manage |
| client auth header | Authorization: Bearer gtk_... |
| token type | Bearer, prefix cgt_ |
| client token lifetime | 43200 seconds by default |
| app token lifetime | 3600 seconds by default |
| minimum password length | 10 characters |
Two kinds of account
A client account owns applications. It signs in at /login, registers apps, and holds a management key (gtk_...). Client sign-ins are strictly rate-limited: the wait doubles after each failure. Read retry_after and back off.
An app user belongs to one application. App users sign in through that app with the app's app_key and app_secret plus their username and password. App user sign-ins are lenient by default: the first few are free, then the wait doubles up to the app's configured cap. A client can tune this per app.
Concepts
- client - a
cidlikec_4m2x9p. Owns apps. Has a management key. - app - an
app_key(public) and anapp_secret(private, shown once). - sub-app - an app with
parent_app_idset. It shares its parent's user pool, so one account works across the family. Opt in per app. - app user - a
uidlikea_9k2m4x. Scoped to one app or app family. Has a password and an optional long-lived key (gak_...). - CGT - the ephemeral token. No refresh tokens: re-run the token request.
The uid is the stable join key for your own database. It is not a secret and it never changes. The token is ephemeral; the uid is not.
Bootstrap in 30 seconds
export GAUTH="https://auth.gumpworks.com"
export API="$GAUTH/api/v1"
export MANAGE="$API/manage"
# 1. sign up a client account at /signup, or ask your administrator
# the response includes a management key: gtk_...
# 2. as that client, create an app
curl -sS -X POST "$MANAGE/apps" \
-H "Authorization: Bearer gtk_..." -H 'Content-Type: application/json' \
-d '{"name":"acme-app","redirect_uris":["https://acme.example/cb"]}'
# -> {"id":1,"app_key":"app_...","app_secret":"gsk_...", ...}
# 3. create an app user
curl -sS -X POST "$MANAGE/apps/1/users" \
-H "Authorization: Bearer gtk_..." -H 'Content-Type: application/json' \
-d '{"app_username":"alice","password":"at-least-10-chars"}'
# -> {"uid":"a_9k2m4x","api_key":"gak_...", ...}
# 4. trade credentials for a CGT
curl -sS -X POST "$API/token" \
-H 'Content-Type: application/json' \
-d '{"grant_type":"password","client_id":"app_...","client_secret":"gsk_...","username":"alice","password":"..."}'
# -> {"access_token":"cgt_...","uid":"a_9k2m4x","app":"app_...", ...}
# 5. use it
curl -sS "$API/userinfo" -H "Authorization: Bearer cgt_..."Public API
Base: https://auth.gumpworks.com/api/v1
POST /token
| grant_type | fields | result |
|---|---|---|
password (client) | username, password | client token, scope client |
password (app user) | client_id, client_secret, username, password | app user token, scope app profile |
client_credentials | client_id, client_secret | app token, no user |
authorization_code | code, client_id, client_secret, redirect_uri | app user token |
# client
curl -sS -X POST "$API/token" -H 'Content-Type: application/json' \
-d '{"grant_type":"password","username":"acme","password":"..."}'
# app user
curl -sS -X POST "$API/token" -H 'Content-Type: application/json' \
-d '{"grant_type":"password","client_id":"app_...","client_secret":"gsk_...","username":"alice","password":"..."}'
# server to server
curl -sS -X POST "$API/token" -H 'Content-Type: application/json' \
-d '{"grant_type":"client_credentials","client_id":"app_...","client_secret":"gsk_..."}'Response:
{
"access_token": "cgt_...",
"certified_gump_token": "cgt_...",
"token_type": "Bearer",
"expires_in": 43200,
"scope": "app profile",
"issued_at": 1761200000,
"sub": "a_9k2m4x",
"uid": "a_9k2m4x",
"app": "app_1a2b3c4d5e6f7a8b",
"app_username": "alice",
"client": "c_4m2x9p"
}GET /userinfo (alias: GET /me)
curl -sS "$API/userinfo" -H "Authorization: Bearer $CGT"{
"active": true,
"sub": "a_9k2m4x",
"uid": "a_9k2m4x",
"app_username": "alice",
"email": "alice@example.com",
"display_name": "Alice",
"status": "active",
"app": "app_1a2b3c4d5e6f7a8b",
"app_name": "acme-app",
"client": "c_4m2x9p",
"scope": "app profile",
"expires_at": 1761243200,
"expires_in": 43199,
"token_kind": "api"
}A client token returns cid instead of uid and app.
PATCH /me, POST /me/password, POST /me/delete-request
Client tokens only.
curl -sS -X PATCH "$API/me" -H "Authorization: Bearer $CGT" \
-H 'Content-Type: application/json' -d '{"email":"new@example.com"}'
curl -sS -X POST "$API/me/password" -H "Authorization: Bearer $CGT" \
-H 'Content-Type: application/json' \
-d '{"current_password":"old","new_password":"new-at-least-10-chars"}'GET /apps
The client's own apps.
curl -sS "$API/apps" -H "Authorization: Bearer $CGT"POST /accounts
Create an app user. A tenant key creates users under its own apps.
curl -sS -X POST "$API/accounts" -H "Authorization: Bearer gtk_..." \
-H 'Content-Type: application/json' \
-d '{"app":"app_...","username":"alice","password":"at-least-10-chars"}'URI style:
curl -sS -X POST "$API/tenants/c_4m2x9p/apps/app_.../accounts" \
-H "Authorization: Bearer gtk_..." -H 'Content-Type: application/json' \
-d '{"username":"alice","password":"at-least-10-chars"}'POST /apps/{app_key}/password
An app user changing their own password.
curl -sS -X POST "$API/apps/app_1a2b3c4d5e6f7a8b/password" \
-H "Authorization: Bearer $CGT" -H 'Content-Type: application/json' \
-d '{"current_password":"old","new_password":"new-at-least-10-chars"}'POST /introspect, POST /revoke
curl -sS -X POST "$API/introspect" -H 'Content-Type: application/json' \
-d '{"token":"cgt_...","client_id":"app_...","client_secret":"gsk_..."}'
curl -sS -X POST "$API/revoke" -H 'Content-Type: application/json' \
-d '{"token":"cgt_..."}'GET /health
Unauthenticated liveness probe.
Management API
Base: https://auth.gumpworks.com/api/v1/manage
Authenticate with Authorization: Bearer gtk_.... A client key can only see and change that client's own apps, users, keys and sessions.
Meta
curl -sS "$MANAGE/health" -H "$AUTH"
curl -sS "$MANAGE/stats" -H "$AUTH"
curl -sS "$MANAGE/settings" -H "$AUTH"Apps
curl -sS "$MANAGE/apps" -H "$AUTH"
curl -sS -X POST "$MANAGE/apps" -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"name":"acme-app","redirect_uris":["https://acme.example/cb"]}'
# a sub-app shares its parent's user pool
curl -sS -X POST "$MANAGE/apps" -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"name":"acme-app-mobile","parent_app_id":1}'
curl -sS -X PATCH "$MANAGE/apps/1" -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"status":"disabled"}'
curl -sS -X POST "$MANAGE/apps/1/rotate-secret" -H "$AUTH"
curl -sS -X DELETE "$MANAGE/apps/1" -H "$AUTH"App users
curl -sS "$MANAGE/apps/1/users" -H "$AUTH"
curl -sS -X POST "$MANAGE/apps/1/users" -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"app_username":"alice","password":"at-least-10-chars"}'
# -> {"uid":"a_9k2m4x","api_key":"gak_...", ...}
curl -sS -X PATCH "$MANAGE/apps/1/users/a_9k2m4x" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"status":"disabled"}'
curl -sS -X POST "$MANAGE/apps/1/users/a_9k2m4x/password" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"password":"new-password-here"}'
curl -sS -X POST "$MANAGE/apps/1/users/a_9k2m4x/rotate-key" -H "$AUTH"
curl -sS -X DELETE "$MANAGE/apps/1/users/a_9k2m4x" -H "$AUTH"Keys
A principal may hold any number of labelled keys. Every key is returned in full exactly once, at creation; afterwards only its hint is visible. Revoke one key without touching the others, or rotate to revoke them all at once.
# client management keys
curl -sS "$MANAGE/clients/1/keys" -H "$AUTH"
curl -sS -X POST "$MANAGE/clients/1/keys" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"label":"ci"}'
# -> {"id":7,"label":"ci","api_key":"gtk_...","warning":"..."}
curl -sS -X DELETE "$MANAGE/clients/1/keys/7" -H "$AUTH"
# app secrets
curl -sS "$MANAGE/apps/1/keys" -H "$AUTH"
curl -sS -X POST "$MANAGE/apps/1/keys" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"label":"staging"}'
curl -sS -X DELETE "$MANAGE/apps/1/keys/8" -H "$AUTH"
# app user keys
curl -sS "$MANAGE/apps/1/users/a_9k2m4x/keys" -H "$AUTH"
curl -sS -X POST "$MANAGE/apps/1/users/a_9k2m4x/keys" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"label":"phone"}'
curl -sS -X DELETE "$MANAGE/apps/1/users/a_9k2m4x/keys/9" -H "$AUTH"rotate-key and rotate-secret are shortcuts for "revoke every key, issue one". Self-service equivalents live at GET|POST /me/keys and DELETE /me/keys/{id} on the public API, authenticated with the caller's own token.
Sessions, audit and lockouts
curl -sS "$MANAGE/sessions" -H "$AUTH"
curl -sS -X DELETE "$MANAGE/sessions/42" -H "$AUTH"
curl -sS "$MANAGE/audit?limit=100" -H "$AUTH"
curl -sS "$MANAGE/security/lockouts" -H "$AUTH"
curl -sS -X POST "$MANAGE/security/unblock" -H "$AUTH" \
-H 'Content-Type: application/json' -d '{"username":"alice"}'Sign-in policy
curl -sS -X PATCH "$MANAGE/settings" -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"app_user_policy":{"start_after":5,"cap":60,"burst_gap":300}}'Bounds: start_after 0-20, cap 5-300 seconds, burst_gap 60-3600 seconds. The server rejects values outside them.
Error format
Every error is a machine code plus one plain sentence.
{"error": "invalid_credentials", "message": "Username or password is incorrect. Next attempt in 4 seconds.", "retry_after": 4}| error | meaning |
|---|---|
invalid_request | a required field is missing or malformed |
invalid_client | unknown app, disabled app, or bad app secret |
invalid_credentials | wrong username or password |
invalid_grant | account disabled, or code expired or used |
invalid_token | missing, expired or revoked CGT |
too_many_attempts | in cooldown; honour retry_after or Retry-After |
weak_password | below the minimum length |
forbidden | the token cannot do that |
not_found | no such client, app or user in your scope |
conflict | the username is already taken |
Rules of engagement
- Never log a
cgt_,gsk_,gtk_orgak_key. - Tokens expire. Treat
expires_inas authoritative and re-authenticate. - One app per service. Do not share an
app_secretbetween services. - Key your own database on
uid, never on the token. app_secret,gtk_andgak_are shown once. Store them properly.- Read
retry_afterand back off. Do not hammer a rate-limited endpoint.