Configuration Reference
This page documents the current runnable configuration surface for graviton-server. The server reads environment variables through ZIO Config.
Scope
This is the current server configuration contract. Other modules may expose additional configuration options that are not wired into the server yet.
TL;DR: pick a backend and set the env vars
Option A: filesystem CAS (default, no external services)
./sbt "server/run"The defaults persist blocks and framed manifests below .graviton/. Set GRAVITON_FS_ROOT or GRAVITON_FS_BLOCK_PREFIX only when you need a different layout.
Option B: MinIO / S3-compatible blocks
export PG_JDBC_URL="jdbc:postgresql://localhost:5432/graviton"
export PG_USERNAME="postgres"
export PG_PASSWORD="postgres"
export GRAVITON_BLOB_BACKEND="minio" # or "s3"
export GRAVITON_S3_ENDPOINT="http://localhost:9000"
export GRAVITON_S3_ACCESS_KEY="minioadmin"
export GRAVITON_S3_SECRET_KEY="minioadmin"
# Optional (defaults shown)
export GRAVITON_S3_BLOCK_BUCKET="graviton-blocks"
export GRAVITON_S3_BLOCK_PREFIX="cas/blocks"
export GRAVITON_S3_REGION="us-east-1"
./sbt "server/run"HTTP endpoints affected by configuration
| Path | Meaning | Notes |
|---|---|---|
GET /api/health/live | liveness | Always available when the process is up |
GET /api/health/ready | backend readiness | Checks block, manifest, resumable-ledger, and staging targets; with Shardcake enabled, also requires the local upload node to own at least one shard |
GET /api/ops/v1/snapshot | current operator state | Fixed, typed health, capacity, placement, durability, dependency, and traffic snapshot |
GET /api/ops/v1/events | operator event stream | Bounded SSE of complete sequenced snapshots; slow subscribers recover from the next snapshot |
GET /metrics | Prometheus scrape | Exposes text/plain; version=0.0.4 (metric names are evolving) |
POST /api/v1/blobs | upload | Uses the selected storage composition |
POST /api/v1/uploads | create resumable upload | Persists a durable filesystem or PostgreSQL checkpoint |
GET /api/v1/uploads/:id or HEAD /api/v1/uploads/:id | resume state | Returns the committed byte offset and expiry |
PATCH /api/v1/uploads/:id | append resumable part | Requires Upload-Offset, Upload-Part-Id, and exact Content-Length |
POST /api/v1/uploads/:id/commit | finalize resumable upload | Streams staged parts through the normal MIME-aware CAS ingest path |
DELETE /api/v1/uploads/:id | cancel resumable upload | Removes the ledger and staged parts |
GET /api/v1/blobs/:id | download | Supports ranges and conditional requests |
HEAD /api/v1/blobs/:id | metadata headers | Checks existence without a response body |
DELETE /api/v1/blobs/:id | logical delete | Removes the manifest and retains shared blocks |
GRAVITON_HEALTH_CHECK_TIMEOUT defaults to 5s and bounds the active storage readiness check. Shardcake placement checks additionally use GRAVITON_SHARDCAKE_SEND_TIMEOUT.
Environment variables
Server
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_HTTP_PORT | 8081 | no | Port for the HTTP server. |
GRAVITON_GRPC_PORT | 9090 | no | Port for the gRPC server. |
GRAVITON_DEPLOYMENT_PROFILE | development | no | Startup policy: development, production, or strict shared-backend production-cluster. |
GRAVITON_HEALTH_CHECK_TIMEOUT | 5s | no | Maximum duration of the active storage readiness check. Must be positive. |
GRAVITON_CHUNK_SIZE | 1048576 | no | Fixed ingest block size in bytes. |
GRAVITON_BLOCK_WRITE_PARALLELISM | 4 | no | Concurrent bounded block writes per ingest. Must be between 1 and 64. |
GRAVITON_DOWNLOAD_WINDOW_REFS | 64 | no | Maximum ordered manifest references buffered ahead of download demand. Must be between 1 and 4096. |
GRAVITON_DOWNLOAD_MAX_IN_FLIGHT | 2 | no | Concurrent verified block fetches per download. Must be between 1 and 16, and no greater than GRAVITON_DOWNLOAD_WINDOW_REFS. |
Local DataStar console
The built-in console is intentionally unauthenticated and therefore disabled by default. When enabled, it is isolated from the API's permissive development CORS middleware, rejects cross-origin browser requests, and binds the combined HTTP listener to 127.0.0.1 by default. Its hypermedia attributes are generated by zio-blocks-datastar; the browser runtime is bundled locally rather than loaded from a CDN. The Operations view consumes the same typed read model as /api/ops/v1/snapshot.
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_CONSOLE_ENABLED | false | no | Mount the local library and Operations UI at /console. Requires GRAVITON_SECURITY_ENABLED=false. |
GRAVITON_CONSOLE_ALLOW_REMOTE_BINDING | false | no | Allow the HTTP listener to bind beyond loopback while the console is enabled. Use only inside a private container network with host ports explicitly published on 127.0.0.1. |
Operator control plane
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_OPERATIONS_REFRESH_INTERVAL | 5s | no | Supervised refresh cadence for the typed operator snapshot. Must be positive. |
GRAVITON_OPERATIONS_EVENT_CAPACITY | 64 | no | Sliding capacity for complete in-process operator events, from 1 through 4096. |
GRAVITON_OPERATIONS_PRESSURE_WARNING_PERCENT | 85 | no | Percentage at which local admission, distributed admission, or PostgreSQL pool pressure becomes degraded. |
The snapshot and SSE endpoints require observability.read when security is enabled. They aggregate fixed operational dimensions and never include payloads, object names, content IDs, tenant IDs, or the raw metrics registry. See Operator Control Plane.
PostgreSQL (required for S3/MinIO or JDBC audit mode)
S3/MinIO mode uses one process-wide HikariCP pool for manifest metadata, tenant policy, quotas, resumable state, audit, and JDBC authorization. Filesystem mode constructs no primary pool unless multi-tenancy or a JDBC security backend requires it. Shardcake placement has a separate, independently bounded pool.
| Name | Default | Required | Meaning |
|---|---|---|---|
PG_JDBC_URL | (none) | yes | JDBC URL for Postgres. |
PG_USERNAME | (none) | yes | Postgres username. |
PG_PASSWORD | (none) | yes | Postgres password. |
PG_POOL_MAX_SIZE | 32 | no | Maximum primary connections per Graviton process. Size the sum across every node below the database and proxy limits. |
PG_POOL_MIN_IDLE | 4 | no | Warm idle primary connections, from zero through maximum pool size. |
PG_POOL_CONNECTION_TIMEOUT_MS | 10000 | no | Bounded wait for a connection. |
PG_POOL_VALIDATION_TIMEOUT_MS | 5000 | no | Connection validation timeout, below the connection timeout. |
PG_POOL_IDLE_TIMEOUT_MS | 600000 | no | Idle connection retirement. |
PG_POOL_MAX_LIFETIME_MS | 1800000 | no | Maximum connection lifetime. Keep below infrastructure termination time. |
PG_POOL_KEEPALIVE_TIME_MS | 120000 | no | PostgreSQL keepalive check cadence. TCP keepalive is also enabled on the driver. |
You must also apply the schema:
PGPASSWORD=postgres \
GRAVITON_DATABASE_URL=postgresql://postgres@localhost:5432/graviton \
./scripts/migrate-postgres.shRepository maintenance coordination
Built-in blob operations hold shared permits for their complete stream lifetime. Garbage collection holds the exclusive form across the complete run. Filesystem mode uses <GRAVITON_FS_ROOT>/cas/.maintenance.lock; S3/MinIO mode uses PostgreSQL session advisory locks.
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_MAINTENANCE_NAMESPACE | graviton | no | Refined non-empty repository lock namespace. Every process sharing one manifest and block repository must use the same value. |
GRAVITON_MAINTENANCE_ACQUISITION_TIMEOUT | 30s | no | Maximum time to wait for a shared permit or exclusive lease. Must be positive. |
GRAVITON_MAINTENANCE_POLL_INTERVAL | 100ms | no | Interruptible backend lock retry interval. Must be positive and no greater than the acquisition timeout. |
The namespace separates repositories that share one PostgreSQL database. Filesystem coordination is rooted by the normalized repository path, so separate roots never share a lock even if they use the same namespace.
Transfer memory admission
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_TRANSFER_MEMORY_MAXIMUM_BUFFERED_BYTES | 536870912 | no | Process-wide weighted byte ceiling shared by active CAS and S3 staging pipelines. Iron-refined from 64 MiB through 1 TiB. |
GRAVITON_TRANSFER_ADMISSION_MAXIMUM_TENANT_BUFFERED_BYTES | 134217728 | no | Live transfer bytes admitted for one authenticated tenant in this process. |
GRAVITON_TRANSFER_ADMISSION_MAXIMUM_CONCURRENT_TENANT_TRANSFERS | 16 | no | Concurrent transfers admitted for one tenant. Iron-refined from 1 through 65,535. |
GRAVITON_TRANSFER_ADMISSION_MAXIMUM_CONCURRENT_BACKEND_TRANSFERS | 64 | no | Concurrent transfers admitted to one physical backend kind in this process. |
GRAVITON_TRANSFER_ADMISSION_MAXIMUM_RESIDENT_TENANTS | 10000 | no | Bound for the process-resident transfer-admission tenant registry. Inactive entries are evicted by age. |
GRAVITON_TRANSFER_ADMISSION_MAXIMUM_RESIDENT_BACKENDS | 64 | no | Bound for the process-resident backend admission registry. |
GRAVITON_TRANSFER_ADMISSION_ACQUISITION_TIMEOUT | 30s | no | Maximum interruptible wait for the complete process, tenant, and backend reservation. |
Each upload composes a named TransferFootprint from input and block queues, chunker working memory, ordered persistence, backend request copies, and replica or erasure fan-out. Each resumable staging part reserves a conservative 128 MiB footprint against the authenticated tenant and filesystem or S3 backend before its request body is demanded. Each download reserves a conservative three-block, 48 MiB ordered-output footprint before it opens the manifest stream or fetches a block. The ordered ZIO mapper's result queue is explicitly one slot rather than its larger library default; GRAVITON_DOWNLOAD_MAX_IN_FLIGHT controls concurrent fetch work without silently increasing retained output. These operations reserve exactly once, in the fixed order process bytes, tenant bytes and concurrency, then backend concurrency. Concurrent transfers wait interruptibly, and every scoped permit is released on success, failure, early termination, or interruption. Size these values below JVM and backend capacity after accounting for direct buffers, database drivers, metrics, and other co-located work.
Cluster-wide transfer admission
The optional graviton-admission-redis provider adds one atomic service, tenant, and backend lease above the hard process-local transfer budget. It coordinates every node in one cell through Redis or Valkey. It carries only counters, hashed tenant and backend keys, lease metadata, and bounded decision events. Upload and download bytes never pass through it.
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_ENABLED | false | no | Enable cluster-wide admission. The single-process default has no Redis dependency. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_CELL_ID | default | when enabled | Refined cell identity used as the Redis Cluster hash tag. Must equal GRAVITON_MULTI_TENANT_CELL_ID in tenant mode. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_HOST | localhost | when enabled | Redis or Valkey primary endpoint. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_PORT | 6379 | when enabled | Redis or Valkey port. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_TLS | false | tenant mode | Enable TLS. Tenant mode rejects startup unless this and certificate verification are enabled. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_VERIFY_CERTIFICATE | true | tenant mode | Verify the server certificate. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_USERNAME | none | no | Optional ACL username. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_PASSWORD | none | tenant mode | AUTH secret. Supply through a secret manager. The Graviton config value is redacted and never attached to logs or typed errors. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_REQUEST_QUEUE_SIZE | 4096 | no | Bounded zio-redis command queue, from 16 through 65536. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_KEY_PREFIX | graviton | no | Alphanumeric, dash, or underscore prefix. Cell-scoped keys remain in one Redis Cluster slot. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_SERVICE_BUFFERED_BYTES | 4294967296 | no | Cluster-wide sum of admitted transfer footprints. Must be at least one process byte budget. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_CONCURRENT_SERVICE_TRANSFERS | 256 | no | Cluster-wide active transfer ceiling. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_TENANT_BUFFERED_BYTES | 536870912 | no | Cluster-wide active bytes for one tenant. Must not exceed the service ceiling. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_CONCURRENT_TENANT_TRANSFERS | 32 | no | Cluster-wide active transfers for one tenant. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_CONCURRENT_BACKEND_TRANSFERS | 192 | no | Cluster-wide active transfers directed at one backend kind. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_LEASE_TTL | 30s | no | Expiring lease lifetime. Must be at least three seconds. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_RENEWAL_INTERVAL | 10s | no | Renewal cadence. Must be no greater than one third of the lease TTL. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_ACQUISITION_TIMEOUT | 10s | no | Maximum provider wait. The local transfer-admission timeout must be at least this long. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_RETRY_INTERVAL | 50ms | no | Interruptible retry cadence after an atomic capacity rejection. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_EVENTS | 100000 | no | Approximate maximum length of the bounded Redis Stream decision log. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_EXPIRED_LEASES_PER_PASS | 256 | no | Work bound for atomic expiry reaping on one command. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_TENANT_REQUESTS_PER_MINUTE | 60000 | no | Atomic authenticated HTTP request contract per tenant and Redis-server-time minute. |
GRAVITON_DISTRIBUTED_ADMISSION_REDIS_MAXIMUM_TENANT_DELIVERED_EGRESS_BYTES_PER_HOUR | 1099511627776 | no | Atomic HTTP bytes-delivered contract per tenant and Redis-server-time hour. Bytes are charged as response chunks leave the server. |
The acquisition order is process bytes, process tenant and backend permits, then the distributed lease. All are acquired before a source socket, manifest stream, or block fetch is demanded. New work fails closed when the coordinator is unavailable. Lease expiry and fencing recover counters after process failure. A coordinator partition cannot revoke bytes already resident in a healthy process, so the local budget remains the authoritative memory boundary; lease loss is logged and counted while the scoped local permit stays held until that transfer exits.
The two traffic-quota settings are currently enforced by authenticated HTTP routes. gRPC continues to use the process-local security rate limiter and does not charge the distributed request or delivered-egress counters.
Manifest authentication
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_MANIFEST_INTEGRITY_REQUIRED | false | no | Require a versioned keyed proof on every filesystem or PostgreSQL manifest and reject an absent or invalid proof before fetching block bytes. |
GRAVITON_MANIFEST_INTEGRITY_KEY_ID | primary | when enabled | Refined identifier persisted with new manifest proofs. |
GRAVITON_MANIFEST_INTEGRITY_HMAC_KEY_BASE64 | none | when enabled | Base64 encoding of the active 32 through 64 byte HMAC key. Supply through a secret manager. |
GRAVITON_MANIFEST_INTEGRITY_PREVIOUS_KEYS_BASE64 | none | no | Comma-separated key-id:base64 verification keys retained during rotation. These keys never sign new manifests. |
Authentication binds the blob content ID, total length, canonical media type, chunker identity, metadata schema, block count, and every ordered block key and byte span. Filesystem repositories persist GVM4; PostgreSQL stores the same bounded metadata and proof in one transaction. This pre-1.0 line deliberately starts from an empty store and does not read legacy manifest formats. Production qualification enables required mode. To rotate, deploy the new active key while retaining the old key in PREVIOUS_KEYS_BASE64, then remove the old verifier only after every reachable old manifest has been replaced or retired. Key material is redacted by configuration values and must never be placed in command arguments or committed files.
Packaged multi-tenant data plane
The verified JWT organization UUID is the tenant key. Policies are durable PostgreSQL rows and callers cannot select a cell, storage domain, or quota. Multi-tenant startup requires S3-compatible storage, security, TLS enforcement, production OIDC, and JDBC audit.
| Name | Default | Meaning |
|---|---|---|
GRAVITON_MULTI_TENANT_ENABLED | false | Route packaged HTTP, gRPC, and Shardcake ingest through authenticated tenant policy. |
GRAVITON_MULTI_TENANT_CELL_ID | default | Operator-owned deployment cell. Policies from another cell are invisible. |
GRAVITON_MULTI_TENANT_MAXIMUM_CACHED_TENANTS | 10000 | Bound for sharded policy, store, and admission registries. |
GRAVITON_MULTI_TENANT_POLICY_CACHE_TTL | 30s | Maximum ordinary policy cache age. Tight revocation should also drain traffic or restart the affected cell. |
GRAVITON_MULTI_TENANT_ADMISSION_TIMEOUT | 10s | Maximum wait for a tenant operation permit. |
GRAVITON_TENANT_STORAGE_ALLOW_SHARED_DEDUPLICATION | false | Server-wide opt-in required in addition to each shared-domain policy. |
Use scripts/provision-tenant.sh with PG_ADMIN_USERNAME and PG_ADMIN_PASSWORD to create or update the cell, lifecycle, sharing scope, object ceiling, retained-byte quota, and concurrency ceiling. Keep that control-plane credential out of the server environment. An exact blob re-upload is quota-idempotent. Publishing or deleting a distinct tenant manifest updates graviton.tenant_storage_usage in the same row-locked transaction.
Block backend selection
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_BLOB_BACKEND | fs | no | Which storage composition to use: fs, minio, or s3. |
Notes:
minioands3select the same S3-compatible adapter.- Set
GRAVITON_S3_ENDPOINTfor an explicit S3-compatible endpoint and credentials, including MinIO or Ceph RGW. - Set an explicit endpoint, access key, and secret key together for MinIO, Ceph RGW, or another S3-compatible service. When no endpoint is set, the AWS SDK uses its default credential provider and
GRAVITON_S3_REGION. - Filesystem mode stores blocks and manifests locally and is the zero-service default.
Resumable upload staging
Resumable routes are mounted by the packaged server for both backends. Filesystem mode writes bounded ZIO Blocks session ledgers under <GRAVITON_FS_ROOT>/cas/upload-sessions and atomic part objects under <GRAVITON_FS_ROOT>/cas/upload-staging. S3/MinIO mode uses the graviton.upload_session and graviton.upload_part PostgreSQL tables plus the configured temporary bucket. The temporary bucket must exist before startup.
| Name | Default | Meaning |
|---|---|---|
GRAVITON_RESUMABLE_UPLOADS_SESSION_TTL | 24h | Lifetime of an open upload checkpoint. |
GRAVITON_RESUMABLE_UPLOADS_PART_LEASE | 15m | Expiring exclusive lease for one idempotent part write. |
GRAVITON_RESUMABLE_UPLOADS_COMMIT_LEASE | 30m | Expiring lease for final CAS ingest. |
GRAVITON_RESUMABLE_UPLOADS_CLEANUP_INTERVAL | 15m | Scoped maintenance cadence for expiry and deferred post-commit cleanup. |
GRAVITON_RESUMABLE_UPLOADS_MAX_PART_BYTES | 268435456 | Maximum bytes in one streamed part, checked incrementally. |
GRAVITON_RESUMABLE_UPLOADS_MAX_PARTS | 8192 | Maximum completed parts in one session. |
GRAVITON_S3_TMP_BUCKET | graviton-tmp | S3-compatible staging bucket in S3/MinIO mode. |
The server never joins parts in memory. It streams each staged object in durable part order into the same declared-size validation, byte sniffing, chunker selection, hashing, and CAS publication used by POST /api/v1/blobs. A committed session retains only its final content ID after cleanup. Maintenance also recognizes a committed ledger with leftover locators, which closes the process-crash window between CAS commit and staging deletion.
Automatic block replication
Leave GRAVITON_REPLICATION_TARGETS empty for one block target. To enable deterministic placement and repair, declare one to sixteen targets using comma-separated name|failure-domain|location records:
# Filesystem locations are independent roots.
export GRAVITON_REPLICATION_TARGETS='west|rack-a|/srv/graviton-a,east|rack-b|/srv/graviton-b'
# In S3-compatible mode, each location is a bucket on its named endpoint.
export GRAVITON_REPLICATION_TARGETS='zone-a|az-a|graviton-blocks-a,zone-b|az-b|graviton-blocks-b,zone-c|az-c|graviton-blocks-c'
export GRAVITON_REPLICATION_TARGET_ZONE_A_ENDPOINT='https://rgw-a.example.com'
export GRAVITON_REPLICATION_TARGET_ZONE_A_ACCESS_KEY='...'
export GRAVITON_REPLICATION_TARGET_ZONE_A_SECRET_KEY='...'
# Repeat the endpoint contract for ZONE_B and ZONE_C.| Name | Default | Meaning |
|---|---|---|
GRAVITON_REPLICATION_TARGETS | empty | Named failure-domain targets. Empty disables the replicated store. |
GRAVITON_REPLICATION_DESIRED_REPLICAS | all configured targets | Stable rendezvous-selected copies per block. |
GRAVITON_REPLICATION_WRITE_QUORUM | desired replica count | Successful target writes required before the manifest may commit. |
GRAVITON_REPLICATION_REPAIR_INTERVAL | 5m | Cadence of the supervised manifest-reference scrub. |
GRAVITON_REPLICATION_REPAIR_BATCH_SIZE | 10000 | Iron-refined maximum referenced blocks per cycle, from 1 through 1,000,000. |
GRAVITON_REPLICATION_MODE | replicated | replicated or fixed erasure-2-1. |
GRAVITON_REPLICATION_LOCAL_FAILURE_DOMAIN | empty | Prefer validated reads from this domain before remote targets. |
Repair progress is durable. Filesystem mode stores its cursor and unresolved failure records below <GRAVITON_FS_ROOT>/cas/repair. S3/MinIO mode stores shared state in graviton.repair_state and graviton.repair_dead_letter PostgreSQL tables.
For each target name, uppercase it and replace hyphens with underscores to obtain its environment prefix. zone-a becomes GRAVITON_REPLICATION_TARGET_ZONE_A. Configure _ENDPOINT, _ACCESS_KEY, _SECRET_KEY, and optionally _REGION. Named targets never inherit GRAVITON_S3_ENDPOINT or its credentials. Startup validation also rejects duplicate target endpoint URLs, because separate buckets on one endpoint are not independently stoppable failure domains.
erasure-2-1 requires exactly three uniquely named targets in three distinct failure domains, DESIRED_REPLICAS=3, and WRITE_QUORUM=2. It stores two systematic data shards and one XOR parity shard. Any two reconstruct the original block. The reconstructed bytes are checked against the original cryptographic content key before they leave the store. This mode is available for S3-compatible backends, including Ceph RGW, and not for filesystem mode.
Target labels are trusted topology declarations. Use distinct physical racks, zones, accounts, clusters, or providers when that is the durability contract. Distinct URLs improve configuration safety but cannot prove the infrastructure behind them is independent. The manifest repository remains GRAVITON_FS_ROOT in filesystem mode or PostgreSQL in S3 mode. Every configured block root or bucket must already exist and be writable.
Filesystem blocks and manifests (GRAVITON_BLOB_BACKEND=fs)
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_FS_ROOT | ./.graviton | no | Root directory for all block data. |
GRAVITON_FS_BLOCK_PREFIX | cas/blocks | no | Subdirectory prefix under GRAVITON_FS_ROOT used for block objects. |
Filesystem layout (exact)
Block files are stored under:
<GRAVITON_FS_ROOT>/<GRAVITON_FS_BLOCK_PREFIX>/<algo>/<hex>-<size>
Example:
./.graviton/cas/blocks/blake3/0123abcd...-1048576
FsBlobManifestRepo stores versioned manifest files under:
<GRAVITON_FS_ROOT>/cas/manifests/<algo>/<hex>-<size>.manifest
S3-compatible blocks (GRAVITON_BLOB_BACKEND=s3|minio)
Endpoint and explicit credentials:
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_S3_ENDPOINT | (none) | only for explicit endpoint | S3-compatible endpoint URL, such as http://localhost:9000. |
GRAVITON_S3_ACCESS_KEY | (none) | with endpoint | Access key id. |
GRAVITON_S3_SECRET_KEY | (none) | with endpoint | Secret access key. |
Block object layout:
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_S3_BLOCK_BUCKET | graviton-blocks | no | Bucket used for block objects. |
GRAVITON_S3_BLOCK_PREFIX | cas/blocks | no | Key prefix for block objects inside the bucket. |
GRAVITON_S3_REGION | us-east-1 | no | Region passed to the AWS SDK client. |
Ceph RGW uses this S3-compatible path. Graviton does not use a native RADOS client and does not rely on Ceph's experimental object deduplication. For multi-site durability, point named targets at independently operated RGW zones and follow Ceph's multi-site guidance: each zone is backed by its own Ceph storage cluster, while a single geographically stretched cluster is discouraged without low-latency networking. Graviton's hosted qualification exercises three independent S3-compatible processes, not three production Ceph clusters, so every Ceph deployment still needs provider acceptance.
Ceph may apply replication or erasure coding inside each target pool. Graviton's erasure-2-1 is a separate cross-target durability layer. Choose both only after calculating the compounded storage, bandwidth, and repair cost. See the Ceph multi-site, CRUSH, and erasure-code documentation.
S3 object key layout (exact)
From S3BlockStore, block objects are written under:
<GRAVITON_S3_BLOCK_PREFIX>/<algo>/<hex>-<size>
Example:
cas/blocks/blake3/0123abcd...-1048576
The S3-compatible endpoint must support PutObject with If-None-Match: *, SHA-256 request checksums, HeadObject, and user metadata. Graviton uses those features to create an immutable content key atomically and to verify duplicate writes without fetching object bodies. Objects without the complete current Graviton proof metadata are rejected.
Quarantined objects use the configured block prefix followed by .graviton-quarantine/. BlockMaintenance.quarantineInventory pages durable recovery receipts without collecting them. Use graviton-operator quarantine-inventory and the dry-run-first quarantine-restore command instead of constructing destination keys.
Bucket creation (MinIO)
You must ensure GRAVITON_S3_BLOCK_BUCKET exists before your first upload.
If you have mc installed:
mc alias set local "$GRAVITON_S3_ENDPOINT" "$GRAVITON_S3_ACCESS_KEY" "$GRAVITON_S3_SECRET_KEY"
mc mb local/"$GRAVITON_S3_BLOCK_BUCKET"If you don’t have mc, you can run it via Docker:
docker run --rm --network host minio/mc \
alias set local "$GRAVITON_S3_ENDPOINT" "$GRAVITON_S3_ACCESS_KEY" "$GRAVITON_S3_SECRET_KEY"
docker run --rm --network host minio/mc \
mb local/"$GRAVITON_S3_BLOCK_BUCKET"Shardcake upload locality
Shardcake is disabled by default. Enable it only when every node uses the same S3-compatible block repository, PostgreSQL manifest database, maintenance namespace, and Shardcake placement database. Filesystem CAS roots are not a shared multi-node topology.
Node configuration:
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_SHARDCAKE_ENABLED | false | no | Mount session-locality routing and the internal streamed owner endpoint. |
GRAVITON_SHARDCAKE_HOST | localhost | when enabled | DNS name or IP reachable by the manager and peer nodes. |
GRAVITON_SHARDCAKE_CONTROL_PORT | 54321 | no | Authenticated Shardcake gRPC control port. |
GRAVITON_SHARDCAKE_UPLOAD_PORT | 54322 | no | Authenticated direct streaming data-plane port. Must differ from the control port. |
GRAVITON_SHARDCAKE_MANAGER_URI | http://localhost:8080/api/graphql | no | Absolute manager GraphQL endpoint. |
GRAVITON_SHARDCAKE_NUMBER_OF_SHARDS | 1024 | no | Stable shard count, from 16 through 65536. Keep it identical across manager and nodes. |
GRAVITON_SHARDCAKE_SERVER_VERSION | development | no | Node compatibility label, 1 through 64 characters. |
GRAVITON_SHARDCAKE_ENTITY_MAX_IDLE_TIME | 5m | no | Idle lifetime for session entities. |
GRAVITON_SHARDCAKE_ENTITY_TERMINATION_TIMEOUT | 10s | no | Grace period for entity termination. |
GRAVITON_SHARDCAKE_SEND_TIMEOUT | 10s | no | Bounded control-message timeout. It does not retry upload bytes. |
GRAVITON_SHARDCAKE_REFRESH_ASSIGNMENTS_RETRY_INTERVAL | 5s | no | Assignment refresh retry interval. |
GRAVITON_SHARDCAKE_REGISTRATION_RETRY_INTERVAL | 500ms | no | Delay between transient manager registration retries while the node control endpoint becomes reachable. |
GRAVITON_SHARDCAKE_REGISTRATION_TIMEOUT | 30s | no | Maximum startup window for node registration before startup fails. |
GRAVITON_SHARDCAKE_UNHEALTHY_POD_REPORT_INTERVAL | 5s | no | Failed-pod report interval. |
GRAVITON_SHARDCAKE_HOT_MAX_SESSIONS | 4096 | no | Maximum reconstructable hot-state entries on one node. |
GRAVITON_SHARDCAKE_INTERNAL_TOKEN | none | yes when enabled | Iron-refined 32 to 256 character internal bearer token. |
Placement storage:
| Name | Default | Required | Meaning |
|---|---|---|---|
GRAVITON_SHARDCAKE_POSTGRES_JDBC_URL | none | yes | JDBC URL for the Shardcake assignment database. |
GRAVITON_SHARDCAKE_POSTGRES_USERNAME | none | yes | Database user with access to the two Shardcake tables. |
GRAVITON_SHARDCAKE_POSTGRES_PASSWORD | none | yes | Database password. |
GRAVITON_SHARDCAKE_POSTGRES_MAXIMUM_POOL_SIZE | 16 | no | Maximum placement connections per manager or node process. |
GRAVITON_SHARDCAKE_POSTGRES_MINIMUM_IDLE | 2 | no | Warm idle placement connections. |
GRAVITON_SHARDCAKE_STORAGE_POLL_INTERVAL | 1s | no | Cross-process assignment observation interval, from 100 ms through 1 minute. |
Manager configuration:
| Name | Default | Meaning |
|---|---|---|
GRAVITON_SHARDCAKE_MANAGER_API_PORT | 8080 | Authenticated manager HTTP port. |
GRAVITON_SHARDCAKE_MANAGER_REBALANCE_INTERVAL | 20s | Normal rebalance cadence. |
GRAVITON_SHARDCAKE_MANAGER_REBALANCE_RETRY_INTERVAL | 10s | Failed rebalance retry cadence. |
GRAVITON_SHARDCAKE_MANAGER_PING_TIMEOUT | 3s | Node health ping timeout. |
GRAVITON_SHARDCAKE_MANAGER_PERSIST_RETRY_INTERVAL | 3s | Durable-state retry interval. |
GRAVITON_SHARDCAKE_MANAGER_PERSIST_RETRY_COUNT | 100 | Maximum persistence retries. |
GRAVITON_SHARDCAKE_MANAGER_REBALANCE_RATE | 0.02 | Fraction of shards moved per rebalance, greater than zero and at most one. |
GRAVITON_SHARDCAKE_MANAGER_POD_HEALTH_CHECK_INTERVAL | 1m | Registered-node health cadence. |
Apply the versioned PostgreSQL migration set with ./scripts/migrate-postgres.sh before starting the manager. V001 creates graviton.shardcake_assignment and graviton.shardcake_pod. The manager holds a PostgreSQL session lease for its complete process lifetime, so a second manager fails at startup instead of competing.
Security
Security is disabled by default. When enabled, issuer and audience are required. Configure an HTTPS JWKS URI for production RS256 verification, or a development shared secret only for local proof.
| Name | Default | Meaning |
|---|---|---|
GRAVITON_SECURITY_ENABLED | false | Require bearer authentication and capability checks. |
GRAVITON_SECURITY_OIDC_ISSUER | none | Exact expected iss claim. |
GRAVITON_SECURITY_OIDC_AUDIENCE | none | Required token audience. |
GRAVITON_SECURITY_OIDC_JWKS_URI | none | Absolute HTTPS JWKS URI for RS256 key lookup and rotation. |
GRAVITON_SECURITY_JWKS_CACHE_TTL | 10m | Remote key cache lifetime. |
GRAVITON_SECURITY_CLOCK_SKEW_SECONDS | 30 | Allowed JWT clock skew. |
GRAVITON_SECURITY_REQUIRE_TLS | false | Reject protected requests outside the configured HTTPS trust boundary. |
GRAVITON_SECURITY_TRUST_PROXY_HEADERS | false | Trust X-Forwarded-Proto and the first literal IP in X-Forwarded-For; enable only behind a proxy that overwrites both headers. |
GRAVITON_SECURITY_CORS_ALLOWED_ORIGINS | empty | Comma-separated exact browser origins. |
GRAVITON_SECURITY_RATE_LIMIT_PER_PRINCIPAL_PER_SEC | 100 | Per-principal request budget. |
GRAVITON_SECURITY_RATE_LIMIT_UPLOAD_BYTES_PER_SEC | 10485760 | Per-principal streamed upload-byte budget. |
GRAVITON_SECURITY_RATE_LIMIT_DOWNLOAD_BYTES_PER_SEC | 52428800 | Per-principal streamed download-byte budget. |
GRAVITON_SECURITY_RATE_LIMIT_MAXIMUM_PRINCIPALS | 100000 | Bound for process-local principal buckets, split across deterministic shards. |
GRAVITON_SECURITY_RATE_LIMIT_IDLE_TTL | 10m | Minimum idle age before a principal bucket may be evicted. |
GRAVITON_SECURITY_MAX_REQUEST_BYTES | 5368709120 | Maximum upload size, enforced while streaming; valid range is 1 byte through 1 TiB. |
GRAVITON_SECURITY_AUDIT_BACKEND | memory | memory or jdbc. |
GRAVITON_SECURITY_AUTHORIZATION_BACKEND | token | JWT capability checks or jdbc ACL augmentation. |
GRAVITON_SECURITY_DEV_SHARED_SECRET | none | Enables HS256 and /dev/token; never set in production. |
/api/stats and /metrics require observability.read when security is enabled. Blob endpoints require the corresponding blob.read, blob.write, or blob.delete capability.
Blob IDs (HTTP)
The HTTP API uses a string BlobId rendered as:
<algo>:<digestHex>:<byteLength>
This is produced on upload by HttpApi from the BinaryKey.Blob:
algo:result.key.bits.algo.primaryName(for example,blake3orsha-256)digestHex:result.key.bits.digest.hex.valuebyteLength:result.key.bits.size
Validation behavior
GET /api/v1/blobs/:idvalidates the id and returns 400 if it cannot be parsed.- Invalid ingest input returns 400 with a JSON error envelope; unexpected storage failures return a generic 500 without exposing arbitrary exception messages.
How configuration is read (source pointers)
- Server port / backend selection:
modules/server/graviton-server/src/main/scala/graviton/server/Main.scala - PostgreSQL env vars for S3/MinIO:
modules/backend/graviton-pg/src/main/scala/graviton/backend/pg/PgDataSource.scala - Filesystem manifest layout:
modules/graviton-runtime/src/main/scala/graviton/runtime/stores/FsBlobManifestRepo.scala - Filesystem block layout:
modules/graviton-runtime/src/main/scala/graviton/runtime/stores/FsBlockStore.scala - S3 block layout:
modules/backend/graviton-s3/src/main/scala/graviton/backend/s3/S3BlockStore.scala - Maintenance configuration:
modules/graviton-runtime/src/main/scala/graviton/runtime/config/MaintenanceConfig.scala - Filesystem coordination:
modules/graviton-runtime/src/main/scala/graviton/runtime/stores/FileMaintenanceCoordinator.scala - PostgreSQL coordination:
modules/backend/graviton-pg/src/main/scala/graviton/backend/pg/PgMaintenanceCoordinator.scala - Metrics endpoint:
modules/protocol/graviton-http/src/main/scala/graviton/protocol/http/MetricsHttpApi.scala - Shardcake node and manager configuration:
modules/integration/graviton-shardcake/src/main/scala/graviton/integration/shardcake/ - Distributed admission configuration:
modules/integration/graviton-admission-redis/src/main/scala/graviton/integration/redis/RedisAdmissionConfig.scala
Common misconfigurations (symptoms → fix)
Missing PostgreSQL schema in S3/MinIO mode
Symptoms: S3/MinIO server startup or uploads fail, or PostgreSQL reports missing relations.
Fix:
PGPASSWORD=postgres \
GRAVITON_DATABASE_URL=postgresql://postgres@localhost:5432/graviton \
./scripts/migrate-postgres.shMinIO endpoint selected but credentials missing
Symptoms: server fails at startup with “Missing env var …”.
Fix: set both GRAVITON_S3_ACCESS_KEY and GRAVITON_S3_SECRET_KEY, unset GRAVITON_S3_ENDPOINT to use the AWS default credential chain, or switch to filesystem blocks.
Bucket does not exist
Symptoms: first upload fails with S3 errors.
Fix: create the bucket (see “Bucket creation (MinIO)” above).