Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -489,10 +489,11 @@ jobs:
run: |
health_headers="$(mktemp)"
health_body="$(mktemp)"
html_headers="$(mktemp)"
html_body="$(mktemp)"
rsc_headers="$(mktemp)"
rsc_body="$(mktemp)"
trap 'rm -f "$health_headers" "$health_body" "$html_body" "$rsc_headers" "$rsc_body"' EXIT
trap 'rm -f "$health_headers" "$health_body" "$html_headers" "$html_body" "$rsc_headers" "$rsc_body"' EXIT

curl --fail --silent --show-error --retry 5 --retry-all-errors --retry-delay 5 \
-H 'Cache-Control: no-cache' -D "$health_headers" -o "$health_body" \
Expand All @@ -501,8 +502,18 @@ jobs:
grep -Eiq '^cache-control:[[:space:]]*no-store([[:space:]]|,|$)' "$health_headers"

curl --fail --silent --show-error --location --retry 5 --retry-all-errors --retry-delay 5 \
-H 'Cache-Control: no-cache' -o "$html_body" https://opensyria.org/ar
-H 'Cache-Control: no-cache' -D "$html_headers" -o "$html_body" https://opensyria.org/
grep -Fq "data-dpl-id=\"${RELEASE_SHA}\"" "$html_body"
discovery_link_count="$(
awk 'BEGIN { count = 0 } {
line = tolower($0)
while (match(line, /\/\.well-known\/api-catalog/)) {
count++
line = substr(line, RSTART + RLENGTH)
}
} END { print count }' "$html_headers"
)"
test "$discovery_link_count" -le 1

rsc_key="deploy-${RELEASE_SHA:0:12}"
curl --fail --silent --show-error --retry 5 --retry-all-errors --retry-delay 5 \
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

### Bug Fixes

* align production rollout probes and registry authentication with the hardened Docker gateway
* prevent duplicated discovery response headers from taking the homepage offline
* include transport and telecom in the well-known API catalog from the shared discovery list
* honor `[skip ci]` consistently in the website CI workflow
* update Next.js and the dependency graph to patched releases and enforce a full audit in verification
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ The site publishes public, read-only discovery metadata for agents:
description, health endpoint, and the shared geography, universities,
transport, and telecom discovery list.
- `/.well-known/agent-skills/index.json` lists the available OpenSyria agent skills.
- Discovery and Markdown route responses carry the HTTP `Link` header directly.
Normal HTML responses expose the same public resources through those stable
routes without injecting a render-wide response header.
- OAuth/OIDC and MCP well-known routes return explicit `404 application/problem+json` responses until OpenSyria offers protected auth flows or a public MCP server. Both `/.well-known/mcp/server-card.json` and the scanner-compatible plural alias `/.well-known/mcp/server-cards.json` use that unsupported response.

## Stack
Expand Down
26 changes: 16 additions & 10 deletions devops/production/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,22 @@ The host identity file is parsed with an exact key allowlist and is never
executed as shell code.

Every Docker operation uses the shared wrapper because the deployment user has
no direct Docker socket access. The wrapper also preserves the temporary
`DOCKER_CONFIG` across privileged pulls without persisting registry credentials.

`switch` changes the shared nginx include, validates/reloads nginx, and checks
`/health` plus `/` through `infra-nginx` with `Host: opensyria.org`. The private
check retries briefly while a graceful nginx reload drains old workers. Shared
nginx changes wait on the cross-application lock instead of failing when another
OpenSyria rollout is finishing. The previous slot is retained.

`finalize` rechecks the private route, drains existing requests, stops the
no direct Docker socket access. The release helper uses only the wrapper's typed
network probe and fixed non-secret Compose status projection. Registry
credentials live in an ephemeral private `DOCKER_CONFIG` that the wrapper copies
for the privileged image pull and the release helper removes on exit.

`prepare` pulls and starts the candidate, then waits for its Docker healthcheck
without changing routing. `switch` changes the shared nginx include atomically,
validates/reloads nginx, and checks uncached public `/health` and homepage GETs.
The routed homepage must stay within an 8 KiB response-header budget and must
not repeat the agent-discovery `Link` set. A failed public check restores and
verifies the previous route. Checks retry briefly while a graceful nginx reload
drains old workers. Shared nginx changes wait on the cross-application lock
instead of failing when another OpenSyria rollout is finishing. The previous
slot is retained.

`finalize` rechecks the public route, drains existing requests, stops the
previous slot, and records the new active state.

`rollback` restores the backed-up nginx include and previous slot. If no prior
Expand Down
Loading