Security
AxiomDB's security model spans authentication, tenant isolation (Row-Level Security), network access control, audit logging, credential audit, CORS policy, and the VPS hardening stack.
Security model layers
Layer 1: Transport HTTPS everywhere, TLS 1.2+
Layer 2: Authentication PASETO v4 (Ed25519) + JWT fallback
Layer 3: Authorization RBAC roles (owner, admin, operator, viewer)
Layer 4: Tenant Isolation Tenant ID scoping + PostgreSQL RLS
Layer 5: Network CIDR allowlisting via square-dbctl + UFW
Layer 6: Audit Immutable event log for all sensitive actions
Layer 7: VPS Hardening fail2ban, crowdsec, opsdc role, NOSUPERUSERAuthentication security
PASETO v4 offline verification
AxiomDB verifies PASETO v4 tokens offline using Ed25519 cryptography. No request to the Square IdP is needed per API call. Token verification steps:
- Split token into
version.purpose.payload_b64.footer_b64 - Validate version is
v4and purpose ispublic - Decode footer, check
alg = "v4.public"andtyp = "paseto" - Extract 64-byte signature from the end of the decoded payload
- Verify using Pre-Authentication Encoding (PAE) against the IdP's Ed25519 key
- Validate
iss,exp,nbf(with 30 second clock skew tolerance) - Extract
token_use = "access"claim - Extract
ctx.tenant_idfor tenant isolation
Token security constraints
| Property | Value |
|---|---|
| Algorithm | Ed25519 (PASETO v4 public) |
| Implicit assertion | square-experience:idp:access:v1 |
| Access token TTL | 15 minutes |
| Refresh token TTL | 7 days |
| Pre-auth token TTL | 5 minutes (2FA login), 60 minutes (2FA setup) |
| Clock skew tolerance | 30 seconds |
Tenant isolation
Every resource in AxiomDB is scoped to a tenant_id derived from the Square IdP's OAuth2 ctx.tenant_id claim. This means users in different organizations can never see each other's data, even if they share the same AxiomDB deployment.
Two-layer enforcement
-
Application-level — Every SQL query includes
WHERE tenant_id = $N. This is the primary enforcement layer, checked for every read and write. -
Database-level — PostgreSQL Row-Level Security (RLS) is enabled on all tables as a safety net. RLS policies use
current_setting('app.current_tenant', true), which is set at the start of every transaction.
-- Example RLS policy
CREATE POLICY tenant_isolation_projects ON projects
USING (tenant_id = current_setting('app.current_tenant', true));Tables with RLS enabled
projectsproject_branchesproject_databasesprovisioning_jobsaudit_eventsnetwork_policiesnetwork_rulesnetwork_apply_eventscredential_events
Background worker bypass
Background workers use a dedicated PostgreSQL role axiomdb_bg_worker with the BYPASSRLS attribute. This role is used only for admin operations (provisioning, cleanup). Application queries always run under the normal role with RLS enforced.
Role-based access control
| Role | Create projects | Read all projects | Delete projects | Manage network | Rotate credentials |
|---|---|---|---|---|---|
owner | ✅ | ✅ | ✅ | ✅ | ✅ |
admin | ✅ | ✅ | ✅ | ✅ | ✅ |
operator | ✅ | ❌ (own only) | ❌ | ✅ | ✅ |
viewer | ❌ | ❌ (own only) | ❌ | ❌ | ❌ |
Role mapping from Square IdP claims:
*.owner→owner*.admin→admin*developer*→operator
Network security
See Network & Access Control for full details. Security highlights:
- Default deny — All new projects start with
mode = restricted(no external access) - CIDR allowlisting — Rules applied to PgBouncer (port 6432) and PostgreSQL
pg_hba.conf(port 5432) via UFW - Expiring rules — Network rules can be set to auto-expire after a TTL
- Smart revocation — Deleting a rule only removes the firewall entry if no other rule covers the same CIDR
Audit logging
Every sensitive operation writes an immutable row to audit_events:
CREATE TABLE audit_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
actor_user_id UUID REFERENCES users(id),
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
tenant_id TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);Audited actions
| Category | Actions |
|---|---|
| Auth | user.login, user.login.cli, user.login.magic, user.signup, 2fa.setup, 2fa.verified |
| Projects | project.created, project.deleted, project.credentials.viewed |
| Branches | branch.created, branch.deleted, branch.credentials.viewed |
| Network | network.rule.created, network.rule.updated, network.rule.deleted, network.public_mode.changed |
| Credentials | credential.rotated |
| Backups | backup.restore.planned, backup.restore.completed |
Query audit events
GET /api/v1/audit?project_id=...&action=branch.created&limit=50
Authorization: Bearer <paseto-v4-token>CORS policy
CORS origins for the ops console are configured per-project. The gateway reads allowed origins from the control-plane DB and applies them as middleware.
GET /api/v1/projects/:project_id/cors
Authorization: Bearer <paseto-v4-token>PUT /api/v1/projects/:project_id/cors
Authorization: Bearer <paseto-v4-token>
Content-Type: application/json
{
"origins": ["https://myapp.com", "https://staging.myapp.com"]
}CORS is not enabled by default. If you do not configure origins, the gateway will reject cross-origin requests from your frontend.
VPS hardening baseline
AxiomDB deployments include the following hardening measures on the VPS:
| Tool | Purpose |
|---|---|
fail2ban | Bans IPs with repeated SSH or Postgres auth failures |
crowdsec | Collaborative threat intelligence, blocks known malicious IPs |
pgbouncer | Connection pooling; limits direct Postgres exposure |
opsdc role | Dedicated OS user for provisioning operations (NOSUPERUSER, NOCREATEDB) |
square-dbctl | Only trusted binary can modify PostgreSQL roles and pg_hba.conf |
| UFW | Host-based firewall; only ports 22, 80, 443, 5432, 6432 exposed |
| TLS | All client connections require sslmode=require |
Credential security best practices
- Never commit credentials. Use
.env.local(gitignored) or a secrets manager. - Use
DATABASE_URLfor runtime. Never exposeDIRECT_URLto app servers — it bypasses PgBouncer. - Rotate credentials after team member offboarding. Use
POST /api/v1/projects/:id/credentials/rotate. - Use expiring network rules for CI. Set
"expires_in": "24h"for temporary CI/CD allowlist entries. - Enable 2FA. Protect your AxiomDB account with TOTP (Settings → Security → Enable 2FA).
How is this guide?
Secrets & Credentials
AxiomDB manages database credentials through the zone.env secrets file on the VPS and exposes them through the gateway API. The credentials API supports retrieval, rotation, and audit logging of every access.
Network Policy
Control network access to your AxiomDB Postgres instances with CIDR allowlists, port-level rules, and progressive security modes.
