Skip to main content

Security

Erold is designed around the principle that secrets must never leave your machine and that no tenant can see another tenant's data — not even through indirect timing attacks.

Client-side redaction (before anything leaves your machine)

The redaction filter is a pure function applied in the hook layer and in the MCP server before any network call. Layers run in order; first match wins per span.

L1 — Path denylist (drop entire event)

Before any event is written to the outbox, the hook checks the file path against a denylist. If it matches, the event is silently dropped — nothing leaves the machine.

  • .env, .env.*, .env.local, .env.*.local
  • *.pem, *.key, *.p12, *.pfx, *.jks, *.keystore
  • id_rsa, id_ed25519, id_ecdsa, *.ppk
  • .ssh/, secrets/, secret/
  • .mcp.json, .netrc, .pgpass
  • kubeconfig, *.kubeconfig
  • terraform.tfvars, *.tfvars (containing "secret" or "key")

L2 — File-type denylist

PKCS12, PEM, x509, PGP keys, octet-stream .key/.bin/.dat files. Base64-looking blobs >100 chars and >80% base64 alphabet are replaced with [REDACTED:binary_blob].

L3 — Entropy detector

Any token replaced with [REDACTED:high_entropy] if: length ≥20, Shannon entropy ≥4.0 bits/char, and contains ≥1 digit + ≥1 uppercase. Catches base64 128-bit keys (~6.0 bits/char) while sparing normal CamelCase prose (~3.5).

L4 — Pattern blocklist

Named patterns for well-known secret formats, case-insensitive where marked:

  • AKIA[0-9A-Z]{16} → [REDACTED:aws_access_key]
  • SCW[A-Z0-9]{20} → [REDACTED:scw_access_key]
  • sk_live_[A-Za-z0-9]{24,} → [REDACTED:stripe_secret_key]
  • ghp_[A-Za-z0-9]{36} → [REDACTED:github_pat]
  • sk-ant-[A-Za-z0-9-_]{93} → [REDACTED:anthropic_key]
  • sk-[A-Za-z0-9]{48} → [REDACTED:openai_key]
  • eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\. → [REDACTED:jwt]
  • -----BEGIN .* KEY----- → [REDACTED:pem_block]
  • (?i)(password|secret|token)=\S{8,} → [REDACTED:generic_secret]

Row-Level Security per tenant

Every table that holds tenant data has Postgres RLS enabled. The app sets SET LOCAL app.tenant_id = '<uuid>' at the start of each request. Policies enforce that every SELECT, INSERT, and UPDATE is scoped to that tenant. A direct asyncpg query without SET LOCAL returns 0 rows — the DB enforces this independently of the application layer.

The integration test suite includes a cross-tenant leak guard: two tenants are seeded, and a query from tenant A must return exactly 0 rows from tenant B's data, even with a direct DB connection.

Idempotency and dedup_hash

Every event the plugin sends carries two deduplication keys:

client_idempotency_key

UUIDv4 generated client-side before any network attempt. Server deduplicates within a project-scoped 72-hour window. Same event replayed 1000 times stores exactly 1 row.

dedup_hash

SHA-256 of project_id + type + content_normalized. Server rejects duplicates within a 30-minute window even if the idempotency key differs. Guards against accidental re-sends from different sessions.

Infrastructure

EU-resident

Backend runs on Scaleway Serverless Containers, fr-par region. Managed RDB Postgres 17 with HA. No US hyperscaler involved.

Private endpoints only

RDB endpoints are private-only (RFC1918). The container connects via Scaleway Private Network. No public DB endpoint.

Secrets in Secret Manager

All credentials live in Scaleway Secret Manager + macOS Keychain. Never in .env files, never in container images, never in logs.

No tracking or analytics

This marketing site has zero analytics trackers. The API has no telemetry that leaves the Scaleway region.

Credential references — not values

Erold never stores credential values. The CredentialReference entity stores a name, the store type (Keychain, Scaleway Secret Manager, or Gitea Secrets), a lookup key, and provisioning instructions. The server rejects write attempts containing high-entropy strings over 32 characters or known secret prefixes (sk-, ghp_, AKIA, etc.) with a 400.

Audit the code yourself

erold-backend is MIT licensed. The redaction filter, RLS policies, and outbox logic are all in the open-source repository. You can run your own instance with full audit visibility.

View source on GitHub