Application API v1
Base URL: https://asi-database-node-api.arybit.co.ke. Application routes require
APPLICATION_API_ENABLED=1 and the selected engine auth stream
(Database/mysql-auth or Database/postgresql-auth). They are absent by default. This is a proposed PHP
integration contract tested against real databases; the PHP source is unavailable.
Each request uses Authorization: Bearer credential_id.secret. A registered,
enabled, unexpired service identity supplies its tenant and explicit scopes.
Callers cannot select tenants, roles, SQL, table names or connection parameters.
No OAuth issuer or token exchange is advertised. The static OpenAPI file is
exported from FastAPI routes with python tools/export-openapi.py; use --check
to detect drift. Production disables interactive schema endpoints.
| Method and route | Scope | Body / result |
|---|---|---|
| GET /health, /healthz, /version, / | none | Process/build diagnostics; gateway redirects / to console |
| GET /ready | none | 200/503 connectivity; application mode checks contract columns and migration checksums |
| GET /api/v1/status | diagnostics:read | Configured engine, server version, schema readiness |
| POST /api/v1/users/lookup | users:read | {email} → user_id,email,username,status,verified |
| GET /api/v1/users/{user_id} | users:read | Same minimal user representation |
| POST /api/v1/users | users:create | {email,username,password_hash} → 201 {user_id} |
| PATCH /api/v1/users/{user_id} | users:update | {username} → {user_id} |
| POST /api/v1/auth/credentials/verify-context | auth:credential-verify | {email} → user_id,status,verified,locked_until |
| POST /api/v1/auth/events | auth:events:write | {user_id,success} → 204 |
| POST /api/v1/sessions | sessions:write | {user_id,token_hash,expires_at} → 201 {session_id} |
| GET /api/v1/sessions/{session_id} | sessions:read | session_id,user_id,expires_at,revoked,active |
| POST /api/v1/sessions/{session_id}/revoke | sessions:revoke | No body → 204; repeat revocation succeeds |
| POST /api/v1/verification-tokens | verification:write | {user_id,token_hash,expires_at,purpose} → 204 |
| POST /api/v1/verification-tokens/consume | verification:consume | {token_hash,password_hash?} → 204 |
UUIDs use validated strings. Emails are ASCII, lowercased before lookup/storage,
with exact binary database comparison. Username accepts 3–50 ASCII letters,
digits or underscores; uniqueness uses binary/C collation on both engines. User
creation accepts encoded PHP bcrypt or Argon2 hashes, never plaintext. The API
stores the hash unchanged. Only asi-authenticate-node-api may hold the credential
status scope. No HTTP route returns password hashes. The actual client password
verification contract is missing; credential context alone cannot authenticate a
password. Existing clients expecting a hash require a reviewed migration before
onboarding. See v14 acceptance limitations.
Eight reported failures lock the user for 15 minutes. Successful events cannot clear an active lock. Session creation requires an active, unlocked user. Session expiry must include a timezone and fall within 30 days; persisted times are UTC. Session reads calculate active from expiry and revocation. A session ID is a lookup identifier, not proof that a presented token is valid: PHP must validate the token cryptographically and bind its session ID. No token validation bypass or raw-token retrieval exists.
Token hashes must be lowercase SHA-256 hex of high-entropy random tokens generated by the consumer. Verification purposes are signup and password_reset; expiry is at most one hour. Consumption locks the owning user and token, rejects expiry or replay, and atomically marks signup verification or updates the password hash. Password reset requires a hash and revokes all user sessions and outstanding password-reset records. Signup rejects a supplied hash. No short numeric-code, refresh exchange, bulk export or arbitrary-table contract is claimed.
Errors have {error:{code,message,request_id}}: 401 invalid identity; 403 missing
scope; 404 missing or foreign-tenant resource; 409 uniqueness, lockout or consumed
token; 413 body over 16 KiB; 422 invalid input; 429 admission limit; 503 database
dependency failure; 500 unexpected failure. Validation errors omit submitted
values. Responses have Cache-Control: no-store and a generated X-Request-ID.
There are no paginated collection endpoints.
The process admits at most 600 application requests per minute, including failed authentication. This bounded global limit is an overload guard, not a distributed per-service quota. Configure gateway quotas for multiple workers/replicas.
There is no Idempotency-Key response cache. User email/username and session/token hash uniqueness prevent duplicate records; repeats return 409. Consumers must reconcile uncertain creates through authorized lookup. Revoke is idempotent; authentication events and one-time consume must never be blindly retried. Only safe lookups may be automatically retried with bounded backoff.
The separate /setup-api/console/ interface uses the operator token, not service
credentials. See console contract. It is outside the application
contract. Versioned changes should be additive; incompatible changes require a
new version and an explicit consumer migration. See PHP integration.
Shared direct data and service quotas
User lookup and username update now call shared database routines; other transaction logic resides in core/application_service.py. Direct TLS access operates on the same rows through separate execute-only, tenant-bound database credentials. See direct interface scope; HTTP callers cannot choose SQL/routines.
In addition to the process overload guard, authenticated requests use a database- coordinated service/tenant quota (API_SERVICE_RATE_PER_MINUTE, default 120). Multiple credentials/workers for one service/tenant share it, so key rotation does not reset the limit. Rejection returns 429 SERVICE_RATE_LIMITED. The fixed-row counter avoids unbounded per-minute history. Invalid identities still hit the process/gateway admission guards. This HTTPS quota does not throttle MySQL protocol connections.