Settings reference
All configuration is environment variables prefixed MEMCOVE_, loaded from the
environment or a .env file. The env var is MEMCOVE_ + the field name uppercased
(e.g. port → MEMCOVE_PORT). dict and list fields are parsed as JSON (e.g.
MEMCOVE_SHARED_SCHEMAS=["ref_market"]).
The defaults target the local Docker stack, so local development needs no changes.
MCP server
| Setting |
Type |
Default |
Notes |
MEMCOVE_HOST |
str |
0.0.0.0 |
bind address |
MEMCOVE_PORT |
int |
8090 |
MCP Streamable HTTP port |
Iceberg REST catalog
| Setting |
Type |
Default |
MEMCOVE_ICEBERG_REST_URI |
str |
http://localhost:8181 |
MEMCOVE_ICEBERG_WAREHOUSE |
str |
s3://warehouse/ |
MEMCOVE_ICEBERG_CATALOG_NAME |
str |
memcove |
Object store (S3 / MinIO)
| Setting |
Type |
Default |
Notes |
MEMCOVE_S3_ENDPOINT |
str |
http://localhost:9000 |
MinIO locally; real S3 in prod |
MEMCOVE_S3_REGION |
str |
us-east-1 |
|
MEMCOVE_S3_ACCESS_KEY |
str | None |
minio |
clear (empty) to use the AWS default credential chain — IRSA / instance profile / STS |
MEMCOVE_S3_SECRET_KEY |
str | None |
minio12345 |
clear alongside the access key for keyless auth |
MEMCOVE_S3_PATH_STYLE |
bool |
true |
|
MEMCOVE_WAREHOUSE_BUCKET |
str |
warehouse |
|
MEMCOVE_STAGING_BUCKET |
str |
memcove-staging |
upload staging; accepts bucket or bucket/sub/path to scope uploads to a prefix |
MEMCOVE_ARTIFACTS_BUCKET |
str |
memcove-artifacts |
exports; accepts bucket or bucket/sub/path to scope exports to a prefix |
Trino (read / derive / export engine)
| Setting |
Type |
Default |
Notes |
MEMCOVE_TRINO_HOST |
str |
localhost |
|
MEMCOVE_TRINO_PORT |
int |
8080 |
|
MEMCOVE_TRINO_USER |
str |
memcove |
service principal (connect identity when not impersonating) |
MEMCOVE_TRINO_CATALOG |
str |
iceberg |
|
MEMCOVE_TRINO_HTTP_SCHEME |
str |
http |
set https for a TLS-fronted Trino |
MEMCOVE_TRINO_IMPERSONATION |
bool |
false |
connect as the tenant so your Trino access control applies per tenant. See Authentication & tenancy. |
MEMCOVE_TRINO_SESSION_PROPERTIES |
dict |
{} |
resource caps etc., e.g. {"query_max_run_time":"60s"} |
Arrow Flight (streaming data plane)
| Setting |
Type |
Default |
Notes |
MEMCOVE_FLIGHT_HOST |
str |
0.0.0.0 |
bind address |
MEMCOVE_FLIGHT_PORT |
int |
8815 |
|
MEMCOVE_FLIGHT_ADVERTISE_URI |
str |
grpc://localhost:8815 |
what clients are told to dial |
MEMCOVE_FLIGHT_TICKET_SECRET |
str |
dev-insecure-change-me |
HMAC signing secret — override in any real deployment. |
MEMCOVE_FLIGHT_TICKET_TTL_SECONDS |
int |
300 |
signed ticket lifetime |
Change the ticket secret before you expose the Flight port
MEMCOVE_FLIGHT_TICKET_SECRET defaults to a known, insecure value. Anyone who can
reach the Flight port with the default secret can forge tenant-scoped tickets. Set a
strong random value in any real deployment; the Flight server logs a loud warning if
you don't.
Registry (Postgres / SQLite / MySQL)
The metadata registry runs on any of three backends, selected by the DSN scheme. See
registry backends for the trade-offs.
| Setting |
Type |
Default |
Notes |
MEMCOVE_REGISTRY_DSN |
str | None |
None |
takes precedence over PG_DSN. Scheme selects the backend: postgresql://…, sqlite:///memcove.db (or sqlite:// in-memory), mysql://… (needs the mysql extra) |
MEMCOVE_PG_DSN |
str |
postgresql://memcove:memcove@localhost:5433/memcove |
fallback when REGISTRY_DSN is unset; host port 5433 avoids clashing with a local pg on 5432 |
MEMCOVE_PG_POOL_MIN_SIZE |
int |
1 |
connections kept warm (Postgres pool) |
MEMCOVE_PG_POOL_MAX_SIZE |
int |
10 |
pool grows to this under load |
MEMCOVE_PG_POOL_TIMEOUT |
float |
10.0 |
seconds to wait for a free connection before erroring |
Reconciler / self-healing
The reconciler diffs the Iceberg catalog against the registry to backfill missing rows and
drop dangling ones; deletion is fail-safe. Tune the guardrails:
| Setting |
Type |
Default |
Notes |
MEMCOVE_RECONCILE_MIN_ABSENT_SWEEPS |
int |
2 |
a row must be absent across this many consecutive sweeps before deletion |
MEMCOVE_RECONCILE_DELETION_CAP_RATIO |
float |
0.25 |
a sweep that would delete more than this fraction of a namespace aborts + alerts |
MEMCOVE_RECONCILE_DELETION_CAP_MIN |
int |
3 |
the ratio cap only applies once a sweep would delete more than this many rows |
Guardrails
| Setting |
Type |
Default |
Notes |
MEMCOVE_PREVIEW_ROW_CAP |
int |
1000 |
max rows returned by query_memory / preview |
MEMCOVE_INLINE_BYTES_CAP |
int |
8388608 |
8 MiB max for inline ingest |
MEMCOVE_EXPORT_ROW_CAP |
int |
5000000 |
safety cap for artifact export |
MEMCOVE_PRESIGN_TTL_SECONDS |
int |
3600 |
presigned URL lifetime |
Tenancy
| Setting |
Type |
Default |
Notes |
MEMCOVE_TENANT_HEADER |
str |
x-memcove-tenant |
direct-mode tenant header |
MEMCOVE_DEFAULT_TENANT |
str |
default |
fallback when the header is absent |
MEMCOVE_TENANT_SUBJECT_HEADER |
str |
"" |
e.g. x-auth-subject; enables the fail-closed provisioning map |
MEMCOVE_TENANT_GROUP_HEADER |
str |
"" |
e.g. x-auth-groups (comma-separated) |
MEMCOVE_TENANT_MAP |
dict |
{} |
subject/group → internal tenant id |
See Authentication & tenancy for how these interact.
Native OAuth 2.1 (resource server)
Off by default (trusted-header / proxy model). When enabled, Memcove validates bearer JWTs
itself against the IdP's JWKS so a client like Claude can connect directly — see
Native OAuth.
| Setting |
Type |
Default |
Notes |
MEMCOVE_OAUTH_ENABLED |
bool |
false |
turn on the native resource server |
MEMCOVE_OAUTH_ISSUER |
str |
"" |
IdP issuer URL, e.g. https://keycloak.example.com/realms/memcove |
MEMCOVE_OAUTH_JWKS_URI |
str |
"" |
optional; derived from the issuer's OIDC discovery when empty |
MEMCOVE_OAUTH_AUDIENCE |
str |
"" |
expected aud; empty = skip the audience check. Set to match your IdP's token audience |
MEMCOVE_OAUTH_REQUIRED_SCOPES |
list |
[] |
scopes every token must carry, e.g. ["memcove.use"] |
MEMCOVE_OAUTH_ALGORITHMS |
list |
["RS256"] |
accepted JWT signing algorithms |
MEMCOVE_OAUTH_TENANT_CLAIM |
str |
sub |
claim used as the tenant id when the identity isn't in TENANT_MAP (map wins, fail-closed, when set) |
MEMCOVE_PUBLIC_URL |
str |
"" |
this server's public URL (OAuth resource id); falls back to http://host:port |
Shared reference plane
| Setting |
Type |
Default |
Notes |
MEMCOVE_SHARED_SCHEMAS |
list |
["ref_market"] |
read-only schemas every tenant may query; never rewritten to a tenant namespace |
Scratchpad plane
An optional fast, small, ephemeral store backed by DuckDB behind Trino — see
Scratchpad. Off by default; needs Trino ≥ 480.
| Setting |
Type |
Default |
Notes |
MEMCOVE_SCRATCH_ENABLED |
bool |
false |
enable target=scratch and the scratch.<label> SQL alias |
MEMCOVE_SCRATCH_CATALOG_MODE |
str |
shared |
shared (one static DuckDB catalog, schema-per-tenant) or per_tenant (a scratch_<tenant> catalog via Trino dynamic catalog management) |
MEMCOVE_SCRATCH_CATALOG |
str |
scratch |
catalog name in shared mode |
MEMCOVE_SCRATCH_CATALOG_PREFIX |
str |
scratch |
per_tenant: catalog is <prefix>_<tenant> |
MEMCOVE_SCRATCH_DUCKDB_DIR |
str |
/data/scratch |
per_tenant: Trino-side dir (on a shared volume) for the <tenant>.duckdb files |
Ingest allowlist
| Setting |
Type |
Default |
Notes |
MEMCOVE_ALLOWED_S3_INGEST_PREFIXES |
list |
[] |
agent s3_parquet URIs must match one of these prefixes; empty = disabled (fail closed) |
Full .env.example
The shipped example, annotated:
.env.example# --- Memcove configuration (copy to .env) ---
# MCP server
MEMCOVE_HOST=0.0.0.0
MEMCOVE_PORT=8090
# Iceberg REST catalog
MEMCOVE_ICEBERG_REST_URI=http://localhost:8181
MEMCOVE_ICEBERG_WAREHOUSE=s3://warehouse/
MEMCOVE_ICEBERG_CATALOG_NAME=memcove
# Object store (MinIO locally; real S3 in prod). Leave the access/secret keys
# EMPTY to use the AWS default credential chain (IRSA / instance profile / STS)
# instead of static keys — the recommended setup on AWS.
MEMCOVE_S3_ENDPOINT=http://localhost:9000
MEMCOVE_S3_REGION=us-east-1
MEMCOVE_S3_ACCESS_KEY=minio
MEMCOVE_S3_SECRET_KEY=minio12345
MEMCOVE_S3_PATH_STYLE=true
# Buckets used by the data plane. STAGING/ARTIFACTS accept either a bare bucket or a
# bucket with a sub-path (e.g. my-bucket/teams/data-eng) to scope uploads/exports to a
# prefix your team owns instead of the bucket root.
MEMCOVE_WAREHOUSE_BUCKET=warehouse
MEMCOVE_STAGING_BUCKET=memcove-staging
MEMCOVE_ARTIFACTS_BUCKET=memcove-artifacts
# MEMCOVE_STAGING_BUCKET=my-bucket/teams/data-eng
# MEMCOVE_ARTIFACTS_BUCKET=my-bucket/teams/data-eng
# Trino (read / derive / export engine)
MEMCOVE_TRINO_HOST=localhost
MEMCOVE_TRINO_PORT=8080
MEMCOVE_TRINO_USER=memcove # service principal (connect identity when not impersonating)
MEMCOVE_TRINO_CATALOG=iceberg
MEMCOVE_TRINO_HTTP_SCHEME=http # set https for a TLS-fronted Trino
# Defense-in-depth: connect to Trino AS the caller's tenant so the operator's own
# Trino access control applies per tenant. Requires impersonation rights on the
# service principal + a configured grant backend. Off = single identity (dev).
MEMCOVE_TRINO_IMPERSONATION=false
# Session properties applied to every data connection (resource caps etc.) — generic
# passthrough; set whatever your Trino version supports. JSON object.
MEMCOVE_TRINO_SESSION_PROPERTIES={}
# e.g. {"query_max_run_time":"60s","query_max_scan_physical_bytes":"10GB"}
# Arrow Flight streaming data plane (M3)
MEMCOVE_FLIGHT_HOST=0.0.0.0
MEMCOVE_FLIGHT_PORT=8815
MEMCOVE_FLIGHT_ADVERTISE_URI=grpc://localhost:8815
# HMAC secret for signing Flight tickets/descriptors so a client cannot forge one for
# another tenant. MUST be overridden in any real deployment (default is insecure).
MEMCOVE_FLIGHT_TICKET_SECRET=dev-insecure-change-me
MEMCOVE_FLIGHT_TICKET_TTL_SECONDS=300 # signed tickets expire after this many seconds
# Control-plane registry. The DSN scheme selects the backend:
# postgresql://… (production default, pooled)
# sqlite:///memcove.db (embedded file — zero-setup local dev, no container)
# sqlite:// (in-memory)
# mysql://user:pass@host/db (needs the `mysql` extra: pip install memcove[mysql])
# MEMCOVE_REGISTRY_DSN takes precedence; MEMCOVE_PG_DSN is kept for compatibility.
# (Host port 5433 avoids clashing with a local postgres on 5432.)
MEMCOVE_PG_DSN=postgresql://memcove:memcove@localhost:5433/memcove
# MEMCOVE_REGISTRY_DSN=sqlite:///memcove.db
# Connection pool sizing (min kept warm, grows to max under load; timeout in seconds
# to wait for a free connection before erroring)
MEMCOVE_PG_POOL_MIN_SIZE=1
MEMCOVE_PG_POOL_MAX_SIZE=10
MEMCOVE_PG_POOL_TIMEOUT=10.0
# Reconciler / read-repair (self-healing). Diffs the Iceberg catalog against the registry
# to backfill missing rows and drop dangling ones; deletion is fail-safe.
MEMCOVE_RECONCILE_MIN_ABSENT_SWEEPS=2 # sweeps a row must be absent before deletion
MEMCOVE_RECONCILE_DELETION_CAP_RATIO=0.25 # abort a sweep that would delete >this fraction of a namespace
MEMCOVE_RECONCILE_DELETION_CAP_MIN=3 # ...but only once it would delete more than this many rows
# Guardrails
MEMCOVE_PREVIEW_ROW_CAP=1000
MEMCOVE_INLINE_BYTES_CAP=8388608 # 8 MiB max for inline ingest
MEMCOVE_EXPORT_ROW_CAP=5000000 # safety cap for artifact export
MEMCOVE_PRESIGN_TTL_SECONDS=3600
# Tenancy. How a caller becomes an internal tenant (applies to both the proxy-header and
# native-OAuth paths):
# auto (default) trust the tenant header (dev) / token claim, or the map if configured
# shared everyone -> ONE tenant (MEMCOVE_SHARED_TENANT, else default). No isolation.
# private every verified identity -> its OWN tenant, hashed so users never collide.
# Needs a trusted identity: MEMCOVE_TENANT_SUBJECT_HEADER (proxy) or OAuth claim.
# mapped explicit MEMCOVE_TENANT_MAP, fail-closed (unmapped identities rejected).
MEMCOVE_TENANT_MODE=auto
MEMCOVE_TENANT_HEADER=x-memcove-tenant # "auto" dev path: client-settable tenant
MEMCOVE_DEFAULT_TENANT=default
MEMCOVE_SHARED_TENANT= # "shared" mode target; empty => default
# Identity sources for mapped/private on the proxy path (your auth proxy sets these; the
# raw tenant header is NOT trusted in those modes).
MEMCOVE_TENANT_SUBJECT_HEADER= # e.g. x-auth-subject
MEMCOVE_TENANT_GROUP_HEADER= # e.g. x-auth-groups (comma-separated)
MEMCOVE_TENANT_MAP={} # "mapped": JSON {"oidc|abc":"acme","team-research":"research"}
# Native OAuth 2.1 (MCP resource server). Off by default (trusted-header / proxy model).
# When enabled, Memcove validates bearer JWTs itself against the IdP's JWKS, so a client
# like Claude can connect directly — no auth proxy needed. Provider-agnostic; Keycloak is
# the documented default (see deploy/keycloak/ and docs/configuration/auth.md).
MEMCOVE_OAUTH_ENABLED=false
MEMCOVE_OAUTH_ISSUER= # e.g. https://keycloak.example.com/realms/memcove
MEMCOVE_OAUTH_JWKS_URI= # optional; derived from the issuer's OIDC discovery if empty
# Expected `aud`; EMPTY (default) skips the audience check. Set it to match your IdP's
# token audience to enforce it (the Keycloak worked example issues `aud=memcove`).
MEMCOVE_OAUTH_AUDIENCE=
MEMCOVE_OAUTH_REQUIRED_SCOPES=[] # JSON list, e.g. ["memcove.use"]
MEMCOVE_OAUTH_ALGORITHMS=["RS256"] # accepted JWT signing algorithms
# Claim used as the tenant id when the identity isn't in MEMCOVE_TENANT_MAP (map wins and
# is fail-closed when set). e.g. sub, preferred_username, or a custom "tenant" claim.
MEMCOVE_OAUTH_TENANT_CLAIM=sub
MEMCOVE_PUBLIC_URL= # this server's public URL (OAuth resource id); defaults to http://host:port
# Shared read-only reference plane (the gateway). Schemas every tenant may SELECT
# from but none may write; not rewritten to the caller's namespace. JSON list.
MEMCOVE_SHARED_SCHEMAS=["ref_market"]
# Scratchpad plane: fast, small, ephemeral DuckDB store BEHIND Trino (needs Trino >= 480
# + a DuckDB catalog). Agents use target=scratch and the scratch.<label> SQL alias, and
# can JOIN scratch with lakehouse + reference in one query. See docs/concepts/scratchpad.md.
MEMCOVE_SCRATCH_ENABLED=false
# "shared" — one static `scratch` DuckDB catalog you configure in Trino (schema-per-
# tenant isolation; single-writer file shared across tenants).
# "per_tenant" — Memcove creates a scratch_<tenant> catalog per tenant via Trino dynamic
# catalog management (catalog.management=dynamic); file-level isolation.
MEMCOVE_SCRATCH_CATALOG_MODE=shared
MEMCOVE_SCRATCH_CATALOG=scratch # catalog name in shared mode
MEMCOVE_SCRATCH_CATALOG_PREFIX=scratch # per_tenant: catalog is scratch_<tenant>
MEMCOVE_SCRATCH_DUCKDB_DIR=/data/scratch # per_tenant: Trino-side dir for <tenant>.duckdb files
# Ingest allowlist for agent-supplied s3_parquet URIs. Empty = feature DISABLED
# (fail closed). JSON list of permitted s3:// prefixes.
MEMCOVE_ALLOWED_S3_INGEST_PREFIXES=[]