Skip to content

Commit 8088a22

Browse files
authored
Merge pull request #5313 from Agenta-AI/fix/bundle-seaweedfs-store
fix(hosting): bundle SeaweedFS store in gh compose
2 parents f31d5f4 + 609cec4 commit 8088a22

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/`)"
@@ -408,6 +410,65 @@ services:
408410
retries: 5
409411
start_period: 5s
410412

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

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -353,17 +353,24 @@ POSTHOG_API_KEY=phc_3urGRy5TL1HhaHnRYL0JSHxJxigRVackhphHtozUmdp
353353
# ================================================================== #
354354
# Object store (durable session mounts)
355355
# ================================================================== #
356-
# Durable S3-compatible store for session/agent mounts. Docs (vars, SeaweedFS setup, namespaces):
356+
# These defaults drive the SeaweedFS object store BUNDLED in this compose file (the `seaweedfs`
357+
# service), so agent/session mounts work out of the box. They are kept consistent with that
358+
# service's own config. To use an external S3-compatible store (AWS S3, Cloudflare R2, MinIO)
359+
# instead, point ENDPOINT_URL/ACCESS_KEY/SECRET_KEY/BUCKET at it and clear SIGNING_KEY (its
360+
# presence selects the bundled-SeaweedFS signing path). Docs (a how-to covers external stores):
357361
# https://docs.agenta.ai/self-host/configuration#store-durable-object-store
358-
# Required: ACCESS_KEY / SECRET_KEY. Commented lines show defaults.
362+
# ACCESS_KEY/SECRET_KEY/SIGNING_KEY are secrets (REPLACE in production!), same pattern as
363+
# AGENTA_AUTH_KEY/AGENTA_CRYPT_KEY above. The `seaweedfs` service and the api/worker/cron all
364+
# read these same values, so any shared replacement still works out of the box. Generate the
365+
# signing key with: openssl rand -base64 32
359366
AGENTA_STORE_ACCESS_KEY=replace-me
360367
AGENTA_STORE_SECRET_KEY=replace-me
361-
# AGENTA_STORE_ENDPOINT_URL=
368+
AGENTA_STORE_ENDPOINT_URL=http://seaweedfs:8333
369+
AGENTA_STORE_BUCKET=agenta-store
370+
AGENTA_STORE_SIGNING_KEY=replace-me
362371
# AGENTA_STORE_STS_ENDPOINT_URL=
363372
# AGENTA_STORE_REGION=us-east-1
364-
# AGENTA_STORE_BUCKET=agenta-store
365373
# AGENTA_STORE_NAMESPACE=
366-
# AGENTA_STORE_SIGNING_KEY=
367374
# AGENTA_STORE_JWT_ISSUER=http://api:8000
368375
# AGENTA_STORE_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
369376
# 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/`)"
@@ -425,6 +427,65 @@ services:
425427
retries: 5
426428
start_period: 5s
427429

430+
seaweedfs:
431+
# === IMAGE ================================================ #
432+
# The bundled durable object store for session/agent mounts. Without it, runner mount
433+
# signing returns 503 and agent file writes are silently lost. Pinned (not :latest) so the
434+
# IAM/STS subsystem stays on a known-good build — it has regressed across releases.
435+
image: chrislusf/seaweedfs:4.37
436+
# === EXECUTION ============================================ #
437+
# ONLY FOR SEAWEEDFS (the bundled store). Real S3/R2/MinIO ship their own STS/IAM and ignore
438+
# all of this. Two configs, both generated from env (no committed files):
439+
# - s3.json: the master identity only (admin; the API holds these creds, never the runner).
440+
# - iam.json: the ADVANCED IAM config — the only path SeaweedFS authorizes STS credentials.
441+
# An OIDC provider points at the API's self-served JWKS; the API mints a short-lived
442+
# RS256 web-identity token and calls AssumeRoleWithWebIdentity to assume `agenta-store`.
443+
entrypoint:
444+
- sh
445+
- -c
446+
- |
447+
cat > /etc/seaweedfs/s3.json <<EOF
448+
{"identities":[{"name":"agenta","credentials":[{"accessKey":"$${AGENTA_STORE_ACCESS_KEY}","secretKey":"$${AGENTA_STORE_SECRET_KEY}"}],"actions":["Admin","Read","Write","List","Tagging"]}]}
449+
EOF
450+
cat > /etc/seaweedfs/iam.json <<EOF
451+
{"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"]}]}}]}
452+
EOF
453+
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
454+
# === CONFIGURATION ======================================== #
455+
env_file:
456+
- ${ENV_FILE:-./.env.oss.gh}
457+
environment:
458+
# The entrypoint bakes these into s3.json/iam.json via raw shell expansion, so they must
459+
# be present in this service's env (defaults here are for the bundled store, the same way
460+
# supertokens carries its own connection URI). Keys come from the env file — secrets never
461+
# get a baked-in default. Override the whole trio via the env file to use S3/R2/MinIO.
462+
AGENTA_STORE_ACCESS_KEY: ${AGENTA_STORE_ACCESS_KEY}
463+
AGENTA_STORE_SECRET_KEY: ${AGENTA_STORE_SECRET_KEY}
464+
AGENTA_STORE_BUCKET: ${AGENTA_STORE_BUCKET:-agenta-store}
465+
AGENTA_STORE_SIGNING_KEY: ${AGENTA_STORE_SIGNING_KEY}
466+
AGENTA_STORE_JWT_ISSUER: ${AGENTA_STORE_JWT_ISSUER:-http://api:8000}
467+
# === STORAGE ============================================== #
468+
volumes:
469+
- seaweed-data:/data
470+
# === NETWORK ============================================== #
471+
networks:
472+
- agenta-oss-gh-network
473+
# Loopback-only by default (never expose the store to a public IP); override AGENTA_STORE_PORT
474+
# to change. In-network services reach it directly at seaweedfs:8333, no publish needed.
475+
ports:
476+
- "${AGENTA_STORE_PORT:-127.0.0.1:8333}:8333"
477+
# === LABELS =============================================== #
478+
labels:
479+
- "traefik.enable=false"
480+
# === LIFECYCLE ============================================ #
481+
restart: always
482+
healthcheck:
483+
test: ["CMD-SHELL", "curl -sf http://localhost:9333/cluster/healthz >/dev/null || exit 1"]
484+
interval: 5s
485+
timeout: 5s
486+
retries: 30
487+
start_period: 5s
488+
428489
traefik:
429490
# === ACTIVATION =========================================== #
430491
profiles:
@@ -540,3 +601,4 @@ volumes:
540601
postgres-data:
541602
redis-volatile-data:
542603
redis-durable-data:
604+
seaweed-data:

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -353,17 +353,24 @@ POSTHOG_API_KEY=phc_hmVSxIjTW1REBHXgj2aw4HW9X6CXb6FzerBgP9XenC7
353353
# ================================================================== #
354354
# Object store (durable session mounts)
355355
# ================================================================== #
356-
# Durable S3-compatible store for session/agent mounts. Docs (vars, SeaweedFS setup, namespaces):
356+
# These defaults drive the SeaweedFS object store BUNDLED in this compose file (the `seaweedfs`
357+
# service), so agent/session mounts work out of the box. They are kept consistent with that
358+
# service's own config. To use an external S3-compatible store (AWS S3, Cloudflare R2, MinIO)
359+
# instead, point ENDPOINT_URL/ACCESS_KEY/SECRET_KEY/BUCKET at it and clear SIGNING_KEY (its
360+
# presence selects the bundled-SeaweedFS signing path). Docs (a how-to covers external stores):
357361
# https://docs.agenta.ai/self-host/configuration#store-durable-object-store
358-
# Required: ACCESS_KEY / SECRET_KEY. Commented lines show defaults.
362+
# ACCESS_KEY/SECRET_KEY/SIGNING_KEY are secrets (REPLACE in production!), same pattern as
363+
# AGENTA_AUTH_KEY/AGENTA_CRYPT_KEY above. The `seaweedfs` service and the api/worker/cron all
364+
# read these same values, so any shared replacement still works out of the box. Generate the
365+
# signing key with: openssl rand -base64 32
359366
AGENTA_STORE_ACCESS_KEY=replace-me
360367
AGENTA_STORE_SECRET_KEY=replace-me
361-
# AGENTA_STORE_ENDPOINT_URL=
368+
AGENTA_STORE_ENDPOINT_URL=http://seaweedfs:8333
369+
AGENTA_STORE_BUCKET=agenta-store
370+
AGENTA_STORE_SIGNING_KEY=replace-me
362371
# AGENTA_STORE_STS_ENDPOINT_URL=
363372
# AGENTA_STORE_REGION=us-east-1
364-
# AGENTA_STORE_BUCKET=agenta-store
365373
# AGENTA_STORE_NAMESPACE=
366-
# AGENTA_STORE_SIGNING_KEY=
367374
# AGENTA_STORE_JWT_ISSUER=http://api:8000
368375
# AGENTA_STORE_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
369376
# AGENTA_WORKER_STREAMS / AGENTA_WORKER_QUEUES: worker topology selectors —

0 commit comments

Comments
 (0)