Skip to content

Commit 609cec4

Browse files
committed
fix(hosting): bundle SeaweedFS object store in the published gh compose
The published OSS/EE gh compose shipped no store, so runner mount signing returned 503 and agent file writes were silently lost. Bundles SeaweedFS (matching the dev stack) and sets working store env defaults. Claude-Session: https://claude.ai/code/session_01XhENr63WL9npkKrJGnzDc1
1 parent 5605cc2 commit 609cec4

4 files changed

Lines changed: 148 additions & 10 deletions

File tree

hosting/docker-compose/ee/docker-compose.gh.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ services:
7777
condition: service_healthy
7878
redis-durable:
7979
condition: service_healthy
80+
seaweedfs:
81+
condition: service_healthy
8082
# === LABELS =============================================== #
8183
labels:
8284
- "traefik.http.routers.api.rule=PathPrefix(`/api/`)"
@@ -392,6 +394,65 @@ services:
392394
retries: 5
393395
start_period: 5s
394396

397+
seaweedfs:
398+
# === IMAGE ================================================ #
399+
# The bundled durable object store for session/agent mounts. Without it, runner mount
400+
# signing returns 503 and agent file writes are silently lost. Pinned (not :latest) so the
401+
# IAM/STS subsystem stays on a known-good build — it has regressed across releases.
402+
image: chrislusf/seaweedfs:4.37
403+
# === EXECUTION ============================================ #
404+
# ONLY FOR SEAWEEDFS (the bundled store). Real S3/R2/MinIO ship their own STS/IAM and ignore
405+
# all of this. Two configs, both generated from env (no committed files):
406+
# - s3.json: the master identity only (admin; the API holds these creds, never the runner).
407+
# - iam.json: the ADVANCED IAM config — the only path SeaweedFS authorizes STS credentials.
408+
# An OIDC provider points at the API's self-served JWKS; the API mints a short-lived
409+
# RS256 web-identity token and calls AssumeRoleWithWebIdentity to assume `agenta-store`.
410+
entrypoint:
411+
- sh
412+
- -c
413+
- |
414+
cat > /etc/seaweedfs/s3.json <<EOF
415+
{"identities":[{"name":"agenta","credentials":[{"accessKey":"$${AGENTA_STORE_ACCESS_KEY}","secretKey":"$${AGENTA_STORE_SECRET_KEY}"}],"actions":["Admin","Read","Write","List","Tagging"]}]}
416+
EOF
417+
cat > /etc/seaweedfs/iam.json <<EOF
418+
{"sts":{"tokenDuration":"1h","maxSessionLength":"12h","issuer":"seaweedfs-sts","signingKey":"$${AGENTA_STORE_SIGNING_KEY}"},"providers":[{"name":"agenta","type":"oidc","enabled":true,"config":{"issuer":"$${AGENTA_STORE_JWT_ISSUER}","clientId":"agenta-store","jwksUri":"$${AGENTA_STORE_JWT_ISSUER}/.well-known/jwks.json"}}],"policies":[{"name":"store-rw","document":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::$${AGENTA_STORE_BUCKET}","arn:aws:s3:::$${AGENTA_STORE_BUCKET}/*"]}]}}],"roles":[{"roleName":"agenta-store","roleArn":"arn:aws:iam::role/agenta-store","attachedPolicies":["store-rw"],"trustPolicy":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":"agenta"},"Action":["sts:AssumeRoleWithWebIdentity"]}]}}]}
419+
EOF
420+
exec weed server -dir=/data -ip=seaweedfs -volume.max=64 -s3 -s3.port=8333 -s3.config=/etc/seaweedfs/s3.json -s3.iam.config=/etc/seaweedfs/iam.json
421+
# === CONFIGURATION ======================================== #
422+
env_file:
423+
- ${ENV_FILE:-./.env.ee.gh}
424+
environment:
425+
# The entrypoint bakes these into s3.json/iam.json via raw shell expansion, so they must
426+
# be present in this service's env (defaults here are for the bundled store, the same way
427+
# supertokens carries its own connection URI). Keys come from the env file — secrets never
428+
# get a baked-in default. Override the whole trio via the env file to use S3/R2/MinIO.
429+
AGENTA_STORE_ACCESS_KEY: ${AGENTA_STORE_ACCESS_KEY}
430+
AGENTA_STORE_SECRET_KEY: ${AGENTA_STORE_SECRET_KEY}
431+
AGENTA_STORE_BUCKET: ${AGENTA_STORE_BUCKET:-agenta-store}
432+
AGENTA_STORE_SIGNING_KEY: ${AGENTA_STORE_SIGNING_KEY}
433+
AGENTA_STORE_JWT_ISSUER: ${AGENTA_STORE_JWT_ISSUER:-http://api:8000}
434+
# === STORAGE ============================================== #
435+
volumes:
436+
- seaweed-data:/data
437+
# === NETWORK ============================================== #
438+
networks:
439+
- agenta-ee-gh-network
440+
# Loopback-only by default (never expose the store to a public IP); override AGENTA_STORE_PORT
441+
# to change. In-network services reach it directly at seaweedfs:8333, no publish needed.
442+
ports:
443+
- "${AGENTA_STORE_PORT:-127.0.0.1:8333}:8333"
444+
# === LABELS =============================================== #
445+
labels:
446+
- "traefik.enable=false"
447+
# === LIFECYCLE ============================================ #
448+
restart: always
449+
healthcheck:
450+
test: ["CMD-SHELL", "curl -sf http://localhost:9333/cluster/healthz >/dev/null || exit 1"]
451+
interval: 5s
452+
timeout: 5s
453+
retries: 30
454+
start_period: 5s
455+
395456
traefik:
396457
# === IMAGE ================================================ #
397458
image: traefik:2
@@ -480,3 +541,4 @@ volumes:
480541
postgres-data:
481542
redis-volatile-data:
482543
redis-durable-data:
544+
seaweed-data:

hosting/docker-compose/ee/env.ee.gh.example

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,24 @@ POSTHOG_API_KEY=phc_3urGRy5TL1HhaHnRYL0JSHxJxigRVackhphHtozUmdp
317317
# ================================================================== #
318318
# Object store (durable session mounts)
319319
# ================================================================== #
320-
# Durable S3-compatible store for session/agent mounts. Docs (vars, SeaweedFS setup, namespaces):
320+
# These defaults drive the SeaweedFS object store BUNDLED in this compose file (the `seaweedfs`
321+
# service), so agent/session mounts work out of the box. They are kept consistent with that
322+
# service's own config. To use an external S3-compatible store (AWS S3, Cloudflare R2, MinIO)
323+
# instead, point ENDPOINT_URL/ACCESS_KEY/SECRET_KEY/BUCKET at it and clear SIGNING_KEY (its
324+
# presence selects the bundled-SeaweedFS signing path). Docs (a how-to covers external stores):
321325
# https://docs.agenta.ai/self-host/configuration#store-durable-object-store
322-
# Required: ACCESS_KEY / SECRET_KEY. Commented lines show defaults.
326+
# ACCESS_KEY/SECRET_KEY/SIGNING_KEY are secrets (REPLACE in production!), same pattern as
327+
# AGENTA_AUTH_KEY/AGENTA_CRYPT_KEY above. The `seaweedfs` service and the api/worker/cron all
328+
# read these same values, so any shared replacement still works out of the box. Generate the
329+
# signing key with: openssl rand -base64 32
323330
AGENTA_STORE_ACCESS_KEY=replace-me
324331
AGENTA_STORE_SECRET_KEY=replace-me
325-
# AGENTA_STORE_ENDPOINT_URL=
332+
AGENTA_STORE_ENDPOINT_URL=http://seaweedfs:8333
333+
AGENTA_STORE_BUCKET=agenta-store
334+
AGENTA_STORE_SIGNING_KEY=replace-me
326335
# AGENTA_STORE_STS_ENDPOINT_URL=
327336
# AGENTA_STORE_REGION=us-east-1
328-
# AGENTA_STORE_BUCKET=agenta-store
329337
# AGENTA_STORE_NAMESPACE=
330-
# AGENTA_STORE_SIGNING_KEY=
331338
# AGENTA_STORE_JWT_ISSUER=http://api:8000
332339
# AGENTA_STORE_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
333340
# AGENTA_WORKER_STREAMS / AGENTA_WORKER_QUEUES: worker topology selectors —

hosting/docker-compose/oss/docker-compose.gh.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ services:
7575
condition: service_healthy
7676
redis-durable:
7777
condition: service_healthy
78+
seaweedfs:
79+
condition: service_healthy
7880
# === LABELS =============================================== #
7981
labels:
8082
- "traefik.http.routers.api.rule=PathPrefix(`/api/`)"
@@ -409,6 +411,65 @@ services:
409411
retries: 5
410412
start_period: 5s
411413

414+
seaweedfs:
415+
# === IMAGE ================================================ #
416+
# The bundled durable object store for session/agent mounts. Without it, runner mount
417+
# signing returns 503 and agent file writes are silently lost. Pinned (not :latest) so the
418+
# IAM/STS subsystem stays on a known-good build — it has regressed across releases.
419+
image: chrislusf/seaweedfs:4.37
420+
# === EXECUTION ============================================ #
421+
# ONLY FOR SEAWEEDFS (the bundled store). Real S3/R2/MinIO ship their own STS/IAM and ignore
422+
# all of this. Two configs, both generated from env (no committed files):
423+
# - s3.json: the master identity only (admin; the API holds these creds, never the runner).
424+
# - iam.json: the ADVANCED IAM config — the only path SeaweedFS authorizes STS credentials.
425+
# An OIDC provider points at the API's self-served JWKS; the API mints a short-lived
426+
# RS256 web-identity token and calls AssumeRoleWithWebIdentity to assume `agenta-store`.
427+
entrypoint:
428+
- sh
429+
- -c
430+
- |
431+
cat > /etc/seaweedfs/s3.json <<EOF
432+
{"identities":[{"name":"agenta","credentials":[{"accessKey":"$${AGENTA_STORE_ACCESS_KEY}","secretKey":"$${AGENTA_STORE_SECRET_KEY}"}],"actions":["Admin","Read","Write","List","Tagging"]}]}
433+
EOF
434+
cat > /etc/seaweedfs/iam.json <<EOF
435+
{"sts":{"tokenDuration":"1h","maxSessionLength":"12h","issuer":"seaweedfs-sts","signingKey":"$${AGENTA_STORE_SIGNING_KEY}"},"providers":[{"name":"agenta","type":"oidc","enabled":true,"config":{"issuer":"$${AGENTA_STORE_JWT_ISSUER}","clientId":"agenta-store","jwksUri":"$${AGENTA_STORE_JWT_ISSUER}/.well-known/jwks.json"}}],"policies":[{"name":"store-rw","document":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::$${AGENTA_STORE_BUCKET}","arn:aws:s3:::$${AGENTA_STORE_BUCKET}/*"]}]}}],"roles":[{"roleName":"agenta-store","roleArn":"arn:aws:iam::role/agenta-store","attachedPolicies":["store-rw"],"trustPolicy":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":"agenta"},"Action":["sts:AssumeRoleWithWebIdentity"]}]}}]}
436+
EOF
437+
exec weed server -dir=/data -ip=seaweedfs -volume.max=64 -s3 -s3.port=8333 -s3.config=/etc/seaweedfs/s3.json -s3.iam.config=/etc/seaweedfs/iam.json
438+
# === CONFIGURATION ======================================== #
439+
env_file:
440+
- ${ENV_FILE:-./.env.oss.gh}
441+
environment:
442+
# The entrypoint bakes these into s3.json/iam.json via raw shell expansion, so they must
443+
# be present in this service's env (defaults here are for the bundled store, the same way
444+
# supertokens carries its own connection URI). Keys come from the env file — secrets never
445+
# get a baked-in default. Override the whole trio via the env file to use S3/R2/MinIO.
446+
AGENTA_STORE_ACCESS_KEY: ${AGENTA_STORE_ACCESS_KEY}
447+
AGENTA_STORE_SECRET_KEY: ${AGENTA_STORE_SECRET_KEY}
448+
AGENTA_STORE_BUCKET: ${AGENTA_STORE_BUCKET:-agenta-store}
449+
AGENTA_STORE_SIGNING_KEY: ${AGENTA_STORE_SIGNING_KEY}
450+
AGENTA_STORE_JWT_ISSUER: ${AGENTA_STORE_JWT_ISSUER:-http://api:8000}
451+
# === STORAGE ============================================== #
452+
volumes:
453+
- seaweed-data:/data
454+
# === NETWORK ============================================== #
455+
networks:
456+
- agenta-oss-gh-network
457+
# Loopback-only by default (never expose the store to a public IP); override AGENTA_STORE_PORT
458+
# to change. In-network services reach it directly at seaweedfs:8333, no publish needed.
459+
ports:
460+
- "${AGENTA_STORE_PORT:-127.0.0.1:8333}:8333"
461+
# === LABELS =============================================== #
462+
labels:
463+
- "traefik.enable=false"
464+
# === LIFECYCLE ============================================ #
465+
restart: always
466+
healthcheck:
467+
test: ["CMD-SHELL", "curl -sf http://localhost:9333/cluster/healthz >/dev/null || exit 1"]
468+
interval: 5s
469+
timeout: 5s
470+
retries: 30
471+
start_period: 5s
472+
412473
traefik:
413474
# === ACTIVATION =========================================== #
414475
profiles:
@@ -524,3 +585,4 @@ volumes:
524585
postgres-data:
525586
redis-volatile-data:
526587
redis-durable-data:
588+
seaweed-data:

hosting/docker-compose/oss/env.oss.gh.example

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,24 @@ POSTHOG_API_KEY=phc_hmVSxIjTW1REBHXgj2aw4HW9X6CXb6FzerBgP9XenC7
317317
# ================================================================== #
318318
# Object store (durable session mounts)
319319
# ================================================================== #
320-
# Durable S3-compatible store for session/agent mounts. Docs (vars, SeaweedFS setup, namespaces):
320+
# These defaults drive the SeaweedFS object store BUNDLED in this compose file (the `seaweedfs`
321+
# service), so agent/session mounts work out of the box. They are kept consistent with that
322+
# service's own config. To use an external S3-compatible store (AWS S3, Cloudflare R2, MinIO)
323+
# instead, point ENDPOINT_URL/ACCESS_KEY/SECRET_KEY/BUCKET at it and clear SIGNING_KEY (its
324+
# presence selects the bundled-SeaweedFS signing path). Docs (a how-to covers external stores):
321325
# https://docs.agenta.ai/self-host/configuration#store-durable-object-store
322-
# Required: ACCESS_KEY / SECRET_KEY. Commented lines show defaults.
326+
# ACCESS_KEY/SECRET_KEY/SIGNING_KEY are secrets (REPLACE in production!), same pattern as
327+
# AGENTA_AUTH_KEY/AGENTA_CRYPT_KEY above. The `seaweedfs` service and the api/worker/cron all
328+
# read these same values, so any shared replacement still works out of the box. Generate the
329+
# signing key with: openssl rand -base64 32
323330
AGENTA_STORE_ACCESS_KEY=replace-me
324331
AGENTA_STORE_SECRET_KEY=replace-me
325-
# AGENTA_STORE_ENDPOINT_URL=
332+
AGENTA_STORE_ENDPOINT_URL=http://seaweedfs:8333
333+
AGENTA_STORE_BUCKET=agenta-store
334+
AGENTA_STORE_SIGNING_KEY=replace-me
326335
# AGENTA_STORE_STS_ENDPOINT_URL=
327336
# AGENTA_STORE_REGION=us-east-1
328-
# AGENTA_STORE_BUCKET=agenta-store
329337
# AGENTA_STORE_NAMESPACE=
330-
# AGENTA_STORE_SIGNING_KEY=
331338
# AGENTA_STORE_JWT_ISSUER=http://api:8000
332339
# AGENTA_STORE_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
333340
# AGENTA_WORKER_STREAMS / AGENTA_WORKER_QUEUES: worker topology selectors —

0 commit comments

Comments
 (0)