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