Security model
Production URL: https://asi-database-node-api.arybit.co.ke
Explicit allowed hosts, deployed HTTPS CORS origins and database transport settings are validated at startup. Deployed environments require private database credentials. Driver exception details are withheld from readiness responses.
Nginx enforces HTTPS, adds security headers and blocks sensitive source paths. Documentation uses local styles and no third-party scripts. Keep database credentials, SSH keys, deployment targets, runtime files, backup keys and logs outside published assets.
The live console uses the private VM operator bearer token, held only in browser memory and sent over HTTPS. The operator service authenticates every request and rejects cross-origin calls. SQL runs under the application OS identity and database role. Lifecycle actions require typed confirmation; operation outcomes are audited without SQL or result values. The FastAPI diagnostic service remains separate. Keep runtime, migration and backup identities separate. Verify PostgreSQL CURRENT_USER tenant policies with representative roles before production acceptance.
Application trust boundary
Opt-in v1 service identities use high-entropy per-service API keys whose SHA-256 hashes are stored in asi_services. This bounded static-key design fits the current single-service infrastructure; no OAuth provider is configured. Keys expire, can be disabled immediately, and carry explicit scopes and one server-assigned tenant. No ordinary application scope grants console, migration or identity-management access. Credential context additionally requires the dedicated authentication service name. Register a different credential ID for rotation, switch the caller, then revoke the old ID:
python backend/service-identity.py register --env-file /secure/identity-admin.env --credential-id auth-v2 --service asi-authenticate-node-api --tenant REVIEWED_UUID --scopes users:read,auth:credential-verify --days 30 --output /secure/auth-v2.key
python backend/service-identity.py revoke --env-file /secure/identity-admin.env --credential-id auth-v1
Choose the minimum scopes after confirming the PHP contract. Output files are exclusive-created mode 0600; commands never print credentials. Keep the parent directory private. The API runtime receives SELECT-only access to the registry; registration and rotation use a separate administrator. Secrets are random, compared in constant time after hashing and never accepted as caller-selected tenant/role assertions. Registry changes take effect on the next request.
The threat model includes stolen service credentials, scope escalation, tenant object ID substitution, injection, body exhaustion, token replay and error leakage. Fixed parameterized SQL, composite tenant foreign keys, per-query tenant predicates, forbidden extra fields, 16 KiB streaming body bounds and transactional token/user locks address those paths. A service remains trusted for its authorized tenant; this API does not authenticate end users on its behalf. Authentication material is returned only by the dedicated protected route. No raw session/reset token is stored, returned or logged. Services must generate high-entropy tokens and protect returned password hashes as credentials.
Application logs contain UTC time and generated correlation IDs, route templates, method, status, service name and elapsed milliseconds. They omit payloads, secrets, email query parameters and database errors. Transactional asi_audit entries record service, tenant, operation and request ID for user changes, credential reads, authentication events, session creation/revocation and token operations. Failed identity/scope requests are recorded by status in the request log; no untrusted service name is logged before authentication. Admission rejects have bounded codes. Database latency is not separately instrumented. Runtime INSERT-only audit grants prevent API users altering history. Operators/DB administrators remain trusted.
The 600-request process admission guard is supplemented by a database-coordinated per-service quota. Deploy gateway controls and private administrative access before internet exposure. The existing VM operator console is still privileged: keep its independent token separate from service keys, restrict the gateway/private network, and prefer its read-only default. Application credentials do not authorize this console. No claim is made that the operator console has been moved onto a private network.
On credential compromise, disable the credential ID, rotate it through a protected channel, revoke affected sessions under a reviewed tenant operation, retain logs, review credential-context access and investigate the runtime database account. DB credential compromise affects every tenant. Protect journal and audit access with operator-only OS/DB grants, encrypted off-host retention and a documented retention window agreed with the owner; no new retention daemon was deployed.
Public direct database policy
The user explicitly authorized any-public-IP reachability for TCP 3306, independently of the previous fixed-source proposal. This does not authorize public administration. The generated /setup-api/ gateway is loopback-only; SSH remains restricted. New direct principals use individual random passwords, REQUIRE SSL, connection caps and execute-only tenant-bound wrappers. The API runtime and direct users are separate. Remote API-to-database connections use verified TLS; disabled verification is rejected. See risk and network review. No production network, account or certificate change was applied. Existing live gateway policy remains unchanged until deployment; the new loopback rule is source code, not live evidence.
Shared routine definitions are installed with a retained non-root definer; direct users cannot execute internal tenant-parameter routines or modify the mapping. MySQL administrators may need elevated migration privileges for binary-logged trigger installation; runtime users never receive those privileges. Public database authentication needs independent failure monitoring/throttling before cutover. Connection caps bound each account's concurrency but do not prove brute-force resistance. Engine patching, observed authentication logs and tested platform/engine abuse protections remain operator acceptance requirements. No claim that MySQL's max_connect_errors limits password guesses is made.