Security

How Propus decides who sees what, and how it protects data at rest and in transit.

Identity

Every engine checks the caller itself; none trusts another engine to have done it.

CredentialStored asNotes
Server admin keySHA-256 hashPrinted once by init; root-only file when installed
Database key (pk_…)SHA-256 hashThe secret in the database's URL; admin for that database; resettable
Role key (pk_…)SHA-256 hashCarries a role, tenant and user
Token (pt_…)signed, not stored30 s to 1 h, bound to its key's role and tenant; revoking the key kills its tokens
Console sessionHTTP-only cookiePassword hashed with Argon2id; one account per server

Row-level security

On for every new table. Roles other than admin see nothing until a policy allows it:

CREATE POLICY own_tenant ON invoices USING (tenant_id = current_setting('propus.tenant'))
  WITH CHECK (tenant_id = current_setting('propus.tenant'));
GRANT SELECT, INSERT, UPDATE ON invoices TO app;

The same tenant boundary holds in FlashCache (a keyspace per database), PulseStream, ObjectCore and TrackSense (tenantColumn). The console's Tables › Access check runs a query as any role and tenant.

Data at rest

  • propus init --encrypt encrypts row values and audit entries with AES-256-GCM (the installer does this by default). The key file is kept apart from the data and its backups; propus rotate-key re-wraps the data key.
  • Secondary-index keys are stored as they sort, so an indexed column's values are readable in the file; index only what may be.
  • Pages are checksummed; damaged data is reported, never served.
  • Backups are integrity-checked before they count and carry a SHA-256 manifest that restore checks.

In transit

  • --tls-cert/--tls-key for HTTPS; --protocol-tls-cert/--protocol-tls-key for the Postgres, Redis and Kafka protocols. Certificates reload when their files change.
  • Kafka clients authenticate with SASL PLAIN; S3 with SigV4 using secrets derived per database.

Replicas

  • A replica authenticates to its primary with the replication secret (kept as a hash on the primary); use HTTPS between them (--primary-ca for a private CA).
  • Replicas receive the data as stored, still encrypted, so a replica needs the primary's key file to read it; its database keys and the console account are the primary's.
  • A replica refuses every write, over every protocol.

The installed server

  • Runs as the unprivileged propus user in a sandboxed systemd service.
  • Keys and settings: admin-key 600 root, master.key, propus.env and the TLS key 640 root:propus.
  • Releases are signed (Ed25519); the installer checks the signature and the checksum before running anything, and rolls back an upgrade that fails its health check.

Not yet

Request rate limits beyond console sign-in, and two-step sign-in for the console.

Reporting a vulnerability

Please report privately to the maintainers rather than in a public issue.