hermes-backup/daily/2026-04-28_203212/skills/devops/arif-os-caddyfile-debug/SKILL.md
Debug 404/502/308 on arifOS surfaces — wrong root path, wrong proxy port, image sync
npx skillsauth add ariffazil/openclaw-workspace arif-os-caddyfile-debugInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
handle path matching with dots — use named matchershandle /.well-known/mcp/server-card.json { file_server } — adapted JSON looks correct but returns 404handle directive path matcher doesn't correctly handle paths with multiple dot-segments# WRONG:
handle /.well-known/mcp/server-card.json {
file_server
}
# CORRECT:
@mcp-server-card {
path /.well-known/mcp/server-card.json
}
handle @mcp-server-card {
file_server
}
# Multiple paths with dots:
@api-mcp {
path /api/mcp/tools.json /api/mcp/resources.json
}
handle @api-mcp {
file_server
}
docker exec caddy cat /var/www/html/arifos/.well-known/mcp/server-card.json returns correct JSON), but external curl returns 404 from Cloudflarecontent-type: text/plain (not application/json) = Cloudflare error page, not Caddydocker exec caddy wget -q -O - --header "Host: arifos.arif-fazil.com" "http://localhost/.well-known/mcp/server-card.json" returns JSON → Caddy is fine, Cloudflare cached old 404cache_purge permission (API returns {"success":false,"errors":[{"code":10000,"message":"Authentication error"}]}), change the URL path instead:
/.well-known/mcp/server-card.json (cached 404)/mcp-server-card.json (same content, fresh route)curl -s "https://api.cloudflare.com/client/v4/user/tokens/verify" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"docker exec caddy cat /etc/caddy/Caddyfile — this is what Caddy is actually usingdocker exec caddy caddy validate --config /etc/caddy/Caddyfile 2>&1 — validates the loaded configdocker exec caddy sh -lc "cat /config/caddy/autosave.json" | python3 -c "..." — see how Caddy interpreted the Caddyfilereload doesn't apply changes: docker restart caddy (full restart, not just reload)/root/arifOS/Caddyfile (bind-mounted as /etc/caddy/Caddyfile in the container)When arifOS surfaces return unexpected 404/502/308, the Caddyfile often has one of three bugs:
/var/www/html/arifOS but mount is /var/www/html/arifos (case mismatch)arifosmcp:3000 but container listens on port 8080# Get container name + image
docker ps --format "{{.Names}}\t{{.Image}}" | grep arif
# Get actual exposed ports (don't trust compose — read the container)
docker inspect <container> --format '{{json .NetworkSettings.Ports}}' | python3 -m json.tool
# Get container networks + IPs
docker inspect <container> --format '{{range $k,$v:=.NetworkSettings.Networks}}{{$k}}:{{.IPAddress}} {{end}}'
CADDY=$(docker ps --format "{{.Names}}" | grep -i caddy | head -1)
# Test with Host header (bypasses Cloudflare cache, no --resolve needed)
docker exec $CADDY wget -q -O - --header "Host: domain.com" http://localhost/path
# Get HTTP status code
docker exec $CADDY wget -q --spider --header "Host: domain.com" http://localhost/path 2>&1 | grep -oP 'HTTP/\d\.\d \K\d+'
# Check what Caddyfile is actually loaded (compare with host source)
docker exec $CADDY cat /etc/caddy/Caddyfile > /tmp/caddy_loaded.conf
diff /root/arifOS/Caddyfile /tmp/caddy_loaded.conf
# Reload Caddy after fix
docker exec $CADDY caddy reload --config /etc/caddy/Caddyfile 2>&1 | grep -E "info|warn|error"
Fix 1: Wrong root path (arifOS/arifos case)
# Wrong:
root * /var/www/html/arifOS
# Correct:
root * /var/www/html/arifos
Fix 2: Wrong proxy port
# Wrong (port mismatch):
reverse_proxy arifosmcp:3000
# Correct (port inside container):
reverse_proxy arifosmcp:8080
Fix 3: 308 redirect from Caddy (file exists but browser redirects)
308 Permanent Redirect when path requires HTTPS and browser hits HTTPcurl -L or wget -L to follow redirect and confirm content is there-L to verifyFix 4: arifOS/arifOS hostname spelling in Caddyfile
/root/arifOS/Caddyfile block at line 139 starts with arifos.arif-fazil.com (no capital O)Fix 5: Cloudflare serves cached 404 even when origin is correct
curl from outside returns "Not Found" with content-type: text/plain;charset=UTF-8 — Cloudflare cache. Inside container, docker exec caddy cat /path/file.json returns correct JSONcf-cache-status or age header present, it's cached. If content-type: text/plain (not application/json), it's definitely Cloudflare's error pagecache_purge permission: change the URL path — rename to a fresh path (/mcp-server-card.json instead of /.well-known/mcp/server-card.json), update Caddyfile, restart Caddy, update all linksdocker exec caddy wget -q -O - --header "Host: arif-fazil.com" "http://localhost/.well-known/arif-human.json" — should return JSON from container if files existFix 6: handle_path directive creates nested subroute handler — use @matcher + uri strip_prefix instead
handle_path /.well-known/* { root * /var/www/html/arif/.well-known file_server } produces correct adapted JSON but still returns 404 in browser (Cloudflare-cached or actual)handle_path is syntactic sugar that wraps the block as a nested subroute. It runs after other matchers in the same site block. The uri strip_prefix /.well-known on a @well-known named matcher + handle @well-known is the cleaner pattern@well-known { path /.well-known/* }
handle @well-known {
uri strip_prefix /.well-known
root * /var/www/html/arif/.well-known
file_server
try_files {path} /index.html
}
/000/* and /999/* chambers — all use named matchers + uri strip_prefixhandle { file_server try_files {path} /index.html }) must be LAST and should not have strip_path_prefix since it serves the root index.htmlFix 7: SPA catch-all eats .json and .txt files
.json files in subdirs return HTTP 200 with HTML "404: Not Found" page instead of JSONtry_files {path} /index.html — when exact path file exists but Caddy can't serve it (wrong root, or path strip issue), the fallback is the index.html page, which itself has a 404uri strip_prefix is properly configured and root is set to the subdir, file_server should serve the exact file before falling back to index.html. If not, check the root path is correctdocker exec caddy cat /var/www/html/arif/999/credentials.json returns JSON), but browser gets HTML with "404: Not Found" title and HTTP 200try_files {path} /index.html — when a file doesn't exactly match, Caddy serves the index.html page with HTTP 200. Even when .json files exist, the exact path match fails if the route structure doesn't explicitly handle the path before the catch-all/.well-known/ route is nested INSIDE the arif-fazil.com block AFTER the _shared handle, not as a top-level catch-all — it never gets a direct handle block, so it falls through to the catch-allhandle /.well-known/* block AND handle /999/*.json block with try_files $uri before the SPA fallback# Add BEFORE the catch-all file_server block:
handle /.well-known/* {
root * /var/www/html/arif
file_server
try_files {path}
}
handle /999/*.json {
root * /var/www/html/arif/999
file_server
content-type application/json
try_files {path}
}
handle /llms.txt needs explicit route since .txt files in root fall to catch-alldocker exec caddy curl -s -o /dev/null -w "%{http_code}" "http://localhost/.well-known/did.json" -H "Host: arif-fazil.com" should return 200, not 308 or 404# Container running different image than compose specifies
docker kill <container>
docker rm -f <container>
cd /root/compose && docker compose up -d arifosmcp
Authoritative Caddyfile: /root/arifOS/Caddyfile (bind-mounted to container as /etc/caddy/Caddyfile)
Sites root: /root/sites/ (bind-mounted to container as /var/www/html)
Compose: /root/compose/docker-compose.yml (desired state, not always live)
arifOS Caddyfile sync gotcha:
docker compose up -d --force-recreate caddy forces a full container recreate so the bind-mounted Caddyfile at /etc/caddy/Caddyfile gets a fresh read from host — plain reload alone was not picking up source-file changes because Caddy re-reads the config file but doesn't re-evaluate the file mount on reloaddiff <(docker exec caddy cat /etc/caddy/Caddyfile) /root/arifOS/Caddyfile.well-known/* caused HTTP 404 at the edge even when Caddy served HTTP 200 — remove Workers blocking those routes via the Cloudflare APIarifosmcp:8080 (NOT 3000 — 3000 is the Docker host port mapping, not the container internal port)/root/arifOS/deployments/af-forge/docker-compose.yml/root/compose/docker-compose.yml is the desired-state compose but NOT what Docker is currently usingfind /root -path "*/af-forge/docker-compose.yml"# 1. Find which compose file created the running container
docker inspect arifosmcp --format '{{index .Config.Labels "com.docker.compose.project.config_files"}}'
# 2. Get the container creation timestamp (to know if it predates any compose changes)
docker inspect arifosmcp --format '{{.Created}}'
# 3. Check live port bindings (don't trust compose — read the container)
docker inspect arifosmcp --format '{{json .HostConfig.PortBindings}}' | python3 -m json.tool
# 4. Check what networks the container is actually on
docker inspect arifosmcp --format '{{range $k,$v:=.NetworkSettings.Networks}}{{$k}}:{{.IPAddress}} {{end}}'
/root/arifOS/Caddyfile (bind-mounted to container as /etc/caddy/Caddyfile, read-only)/root/sites/ (bind-mounted to container as /var/www/html)/root/arifOS/deployments/af-forge/docker-compose.yml/root/compose/docker-compose.yml/root/arifOS/deployments/af-forge/docker-compose.ymlAfter any Caddyfile fix:
adapted config to JSON)<title>arifOS // Sovereign Compute Federation</title> 200/000 — Wisdom Wall for AI 200/999 — Verification Room for AI 200/999 — Verification Room for AI 200development
Check every skill’s “use when” and “do not use when” clauses for collisions, missing negatives, and vague verbs like “help,” “assist,” or “improve.” Load when linting, reviewing, or validating trigger boundaries.
development
Bootstrap, design, and package new skills. Load when capturing user intent for a new skill or drafting its initial instruction framework.
content-media
Diagnose which federation services are up, down, or drifting. Produce a prioritized remediation plan.
business
Scan a repo or workspace for exposed secrets, tokens, keys, and credentials. Produce a findings report with remediation steps.