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