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