@@ -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 :
0 commit comments