The api vhost has two branches in proxy.conf.j2: with SSO enabled it proxies
to the Keycloak SSO proxy, so an unauthenticated request is redirected; only
the else branch answers `return 403`. The expected status codes covered just
the second case, so the webserver health unit reported
api.seaweedfs.s3.infinito.example: ERROR: Expected [403, 405]. Got 301.
and, because that unit grades the whole round, every deploy ended with
failed=1 while the applications themselves were fine.
The sibling endpoints already allow the redirect: filer and master both list
302 next to 403. Add 301 and 302 to api.
Not established: why api answers 301 where the siblings declare 302. A
permanent redirect may come from a slash or scheme normalisation ahead of the
SSO proxy rather than from the proxy itself; both codes are accepted here
instead of chasing that down.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Autoformat (mbake/yamllint) re-indented sequence items under their mapping key
across the regenerated meta files and wrapped one long URL scalar in the matomo
inject task. Whitespace/formatting only, no semantic change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the csp, domains, and networks blocks out of roles/*/meta/server.yml
into dedicated meta files, each mapping to its own top-level applications key
via the recursive meta-walk:
csp: -> meta/csp.yml -> applications.<app>.csp
domains: -> meta/domains.yml -> applications.<app>.domains
networks: -> meta/networks.yml -> applications.<app>.networks
Residual nginx/proxy keys (status_codes, client_max_body_size, locations,
ip6_enabled, config_upstream_url) stay in server.yml; an emptied server.yml is
deleted. 98 server.yml -> 249 new files, 74 deleted.
- Consumers repointed applications.<app>.server.{csp,domains,networks} ->
applications.<app>.{csp,domains,networks} across templates, vars, tasks,
lookups and filters.
- Infra readers migrated: canonical_domains_map, application_domain_index,
build_ldap_role_entries, service-registry overlay discovery,
roles_with_service, and the file-based readers utils/domains/list.py +
utils/meta/scan.py (read the now-deleted server.yml directly).
- Detection + mapping: _META_TOPICS gains csp/domains/networks;
ROLE_FILE_META_{CSP,DOMAINS,NETWORKS} constants + ROLE_FILES entries.
- New self-expiring lint guard (test_server_meta_no_split_fields) fails if a
server.yml still carries a split field; auto-fails from 2026-12-28 as a
delete reminder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve 69 conflicts across ~70 files: union main's image-version bumps and new service fields (bond, resource limits) with the swarm branch's default_placement / NFS / IS_STACK_HOST config; retire the three per-deploy-type test workflows main still edited (consolidated into compose+swarm); adopt main's run-once 01_core dispatchers while re-applying each role's swarm guard. Fix the post-merge lint surfaced by main's stricter rules: bond + resource-budget keys, container-exec stack-host gating + resolver routing, leaky/dead proxy vars, suitecrm once-flag, xwiki variant nocheck, nextcloud oversized-block to gated include, matrix-mdad capped at 3g. make autoformat clean, make test green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
web-app-seaweedfs overrides meta/server.yml without a client_max_body_size key.
Consumers that resolve webserver_client_max_body_size via
lookup('config', application_id, 'server.client_max_body_size') (e.g.
web-app-matrix, whose lazy role var is evaluated while seaweedfs' own proxy is
rendered with application_id=web-app-seaweedfs) then hit a hard lookup failure.
Declare the field on seaweedfs itself (0 = unbounded, matching its S3 object
store nature and the proxy's api branch) instead of papering over it with a
lookup default, so every consumer resolves seaweedfs' own value.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The net refactor (366dc7b70/cb27ff375) migrated every shared-net provider (postgres/mariadb/openldap/ollama/openresty/keycloak) to the overlay registry but missed seaweedfs and minio, so an object-store consumer like baserow never joined the provider's docker network and its S3 client hit ECONNREFUSED on seaweedfs:8333 (the intended direct data-plane path).
Add networks.overlay to both engines so consumers attach, and complete the multi-service shared-net-provider contract: (1) render.py emits a bare auto-IPAM project default for an own shared_net provider (no name, no subnet) so the entity-named external net created by network/create.yml is not re-requested -- this caused 'invalid pool request: Pool overlaps' on the first attempt. (2) a provider_self_alias flag lets the provider's sidecars (proxy/sso-proxy) JOIN the shared net WITHOUT publishing the <entity> alias, so the name resolves only to the main container and baserow's :8333 never round-robins onto a port-less sidecar. (3) seaweedfs/minio 01_core pre-create the external <entity> net via network/create.yml (mirroring web-app-prometheus) so compose adopts it as the project default instead of building <entity>_default.
Verified: baserow compose e2e test-seaweedfs.js passes (upload stored in the bucket, 3.9s); new TestOwnSharedNetProvider locks the contract; full make test green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reconcile main's object-store feature and pre-swarm deploy tasks with the branch's compose/networks/volumes SPOT and swarm lints.
objstore (seaweedfs/minio): standalone compose + embedded service templates migrated to the container_image/container_volumes/container_networks/compose_volumes/compose_networks/depends_on lookups; meta/volumes.yml on the canonical dict schema with s3.json and nginx.conf as type:config; embedded services made swarm-compatible via compose_only/compose_restart.
deploy tasks (erpnext, opentalk, seaweedfs, shopware, snipe-it, wordpress, yourls): container exec routed through the container_address resolver and gated on IS_STACK_HOST, shell module plus stack_host_template, no chdir or compose-verb forms.
Also removed orphaned vars, restored MAILU_INSTANCE_DIR, freed the seaweedfs subnet (192.168.206.0/24), stripped literal quotes from the nextcloud/bookwyrm env templates, and routed main's objstore compose_volumes tests through dir_var_lib.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sys-ctl-hlth-webserver checker HEAD-requests each domain. The ungated S3 api endpoint returns 405 (Method Not Allowed) for HEAD on the service root, and 403 for anonymous access — both mean a healthy, secured gateway. A 200 (anonymous ListBuckets) can never match a HEAD probe, so the prior expectation made the health service exit non-zero and abort deploys. Verified in-container: the checker's CA-verified HEAD returns 405, which is in [403, 405].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The webserver health check expected 403 on api.<base>, but the ungated S3 gateway answers an unauthenticated GET / with 200 and an empty ListAllMyBucketsResult (anonymous sees no buckets). The wrong expectation failed hlth-webserver once the Playwright stage stopped masking it. Expect 200 to match the live S3 root response; filer/master keep [302, 403].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three deploy-validated fixes for the oauth2-gated Filer/Master UIs: (1) make filer the primary canonical domain so the oauth2-proxy redirect_url/callback lands on a gated domain instead of the ungated S3 api host; (2) point the sso-proxy upstream at the unique container name seaweedfs-proxy (and give the nginx proxy that hostname) so it no longer resolves a foreign app's generic 'proxy' container over a shared network, which had round-robined admin traffic to nextcloud; (3) decode the playwright env via the shared decodeDotenvQuotedValue so the administrator password's escaped characters are read correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a database-style object-store abstraction: the objstore lookup resolves the active engine (seaweedfs|minio) per consumer and yields a uniform S3 payload, objstore_secret_key is auto-generated like database_password, sys-svc-objstore delegates per-consumer provisioning to the resolved provider's objstore_init entrypoint (lazy-loading the provider when it was not preloaded), and compose/network/volume plumbing supports both a shared central instance and an embedded storage-only instance per consumer.
Add the web-app-seaweedfs provider role: Filer and Master UIs behind the admin-only oauth2-proxy via a role-local nginx and proxy.conf.j2 (S3 API domain stays ungated, admin UIs deny with 403 when SSO is disabled), per-consumer S3 identities from s3.json, idempotent bucket provisioning via weed shell, a /24 on the central-provider subnet range, Playwright coverage for admin/biber/guest plus consumer bucket existence, and unit tests for the objstore lookup and the per-engine compose volumes.
Mark web-app-minio lifecycle: unsupported, move its mc client image into a dedicated services.client entry, align it with the objstore credential contract, and make its consumer provisioning idempotent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sys-ctl-hlth-webserver checker HEAD-requests each domain. The ungated S3 api endpoint returns 405 (Method Not Allowed) for HEAD on the service root, and 403 for anonymous access — both mean a healthy, secured gateway. A 200 (anonymous ListBuckets) can never match a HEAD probe, so the prior expectation made the health service exit non-zero and abort deploys. Verified in-container: the checker's CA-verified HEAD returns 405, which is in [403, 405].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The webserver health check expected 403 on api.<base>, but the ungated S3 gateway answers an unauthenticated GET / with 200 and an empty ListAllMyBucketsResult (anonymous sees no buckets). The wrong expectation failed hlth-webserver once the Playwright stage stopped masking it. Expect 200 to match the live S3 root response; filer/master keep [302, 403].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three deploy-validated fixes for the oauth2-gated Filer/Master UIs: (1) make filer the primary canonical domain so the oauth2-proxy redirect_url/callback lands on a gated domain instead of the ungated S3 api host; (2) point the sso-proxy upstream at the unique container name seaweedfs-proxy (and give the nginx proxy that hostname) so it no longer resolves a foreign app's generic 'proxy' container over a shared network, which had round-robined admin traffic to nextcloud; (3) decode the playwright env via the shared decodeDotenvQuotedValue so the administrator password's escaped characters are read correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a database-style object-store abstraction: the objstore lookup resolves the active engine (seaweedfs|minio) per consumer and yields a uniform S3 payload, objstore_secret_key is auto-generated like database_password, sys-svc-objstore delegates per-consumer provisioning to the resolved provider's objstore_init entrypoint (lazy-loading the provider when it was not preloaded), and compose/network/volume plumbing supports both a shared central instance and an embedded storage-only instance per consumer.
Add the web-app-seaweedfs provider role: Filer and Master UIs behind the admin-only oauth2-proxy via a role-local nginx and proxy.conf.j2 (S3 API domain stays ungated, admin UIs deny with 403 when SSO is disabled), per-consumer S3 identities from s3.json, idempotent bucket provisioning via weed shell, a /24 on the central-provider subnet range, Playwright coverage for admin/biber/guest plus consumer bucket existence, and unit tests for the objstore lookup and the per-engine compose volumes.
Mark web-app-minio lifecycle: unsupported, move its mc client image into a dedicated services.client entry, align it with the objstore credential contract, and make its consumer provisioning idempotent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>