Security
Credential protection, tenant isolation, and webhook trust.
Secrets and credentials
API key secrets are returned once and stored only as peppered HMAC digests. Customer-owned Xero app credentials, Xero tokens, customer webhook signing secrets, and raw provider payloads use AES-256-GCM envelope encryption. In production, data keys are wrapped by Google Cloud KMS; local development derives a key from the configured development secret.
Never log bearer keys, OAuth tokens, raw accounting payloads, signing secrets, authorization codes, or document contents.
Cloud Tasks worker delivery uses both provider-signed OIDC requests and a separately generated
ACCOUNTING_WORKER_AUTH_TOKEN; the token is never reused for API keys or provider credentials.
Tenant isolation
Every repository query that touches product data includes organization and environment ownership. The environment is selected by the API key, not by request input. Console mutations require an authenticated organization membership and explicit accounting permission.
OAuth
Connect sessions are short-lived, single-use, and carry high-entropy state. Their server-side record binds organization, environment, Xero app, customer identifier, and retention mode. Redirect URIs are checked against the configured allowlist. Connections with ambiguous Xero tenants remain pending until the customer selects one.
Webhooks
Inbound Xero and outbound customer signatures are verified against raw bytes with constant-time comparison. Event IDs are persisted for deduplication. Customer destinations must use public HTTPS on port 443. Delivery resolves and validates every address, rejects private and reserved ranges, and pins the validated address for the request to prevent DNS rebinding.
Retention and auditability
Cached normalized data contains no raw provider payload; encrypted raw provider payloads expire after 30 days. A zero-retention connection persists neither representation and supports live reads only. Disconnect clears tokens, provider identifiers, cached accounting data, and payload-bearing operational records. Idempotency records expire after 30 days. Audit events, redacted operation state, immutable usage events, and commercial terms remain available for incident reconstruction. Production infrastructure should apply least-privilege service accounts, private worker ingress, managed Postgres backups, KMS rotation, and centralized log retention.