feat(aosguard): a guarded Netlify domain-alias surface, so the token stays inside #1355
No reviewers
Labels
No labels
burndown-2026-06
burndown-2026-08
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agentic-os!1355
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/mt75-netlify-wrap"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1353. Kai confirmed the route directly.
Why a wrap
There was no netlify surface at all - no guardfile, nothing installed. The alternative to building one was fetching
/coilysiren/netlify/tokeninto a caller's environment. This keeps it where the Forgejo surfaces keep theirs: resolved from SSM at exec time via thessmvalue provider, never in argv, never in a shell.Two leaves,
site(read) andalias(write), both running one packaged module, so no other part of the Netlify API is reachable.The hazard Vera raised, and why it is not answered by experiment
domain_aliaseswas flagged as likely replace rather than merge, meaning a wrap taking one alias would silently delete the others.That cannot be settled by measurement: the experiment is a production write that re-issues a live certificate. So the module does not depend on the answer. Every write is a read-modify-write that sends the union of existing and new aliases, which is correct under either semantic.
Two things found by looking rather than assuming:
domain_aliasesis currently[], so nothing is at risk today. The trap arms on the second alias, which is exactly the three-alias sequence #1353 describes.custom_domain(www.coilysiren.me) is a separate field, so an alias write cannot clobber the primary domain's record. The certificate concern is real and unrelated to this.Batching is therefore a safety property, not tidiness: adding a domain re-issues the certificate covering every name on the site, so three calls are three certificate events on a live site.
--siteis mandatory and allowlisted#1349 closed a wrap that documented a fixed target while accepting any. A new surface is exactly where that class comes back, so the acceptance criterion is built in rather than filed later. The site is named in full because the API's other accepted form is an opaque uuid, which has no business in a tracked file.
Verified against the built binary
Testing found two real bugs before they shipped:
embedappends the script path last, so pinning the action inargvproducedpython3 -I -- show <script>- python would have runshowas the program. The action moved to a guardedarg0, one per leaf.Deny paths, each with its own message:
--siteaddwords on thesiteleafaddwith no--aliasThe read path was exercised end to end through the guard and returns the live site. A network failure now names itself rather than printing a traceback, which is the #1346 lesson applied at authoring time.
Not done here
The aliases are not added. That write touches a live certificate covering the primary domain and wants someone watching it. The command is ready:
One call, all three, per the batching rule above.
Docs
In
docs/aosguard.mdrather than a new page, becausedocs/*.mdis at its 40-file cap (#1341). The section displaced retold prose in the same file and normalized two hard-wrapped paragraphs, the practice AGENTS.md already documents.Verification
uv run pytest748 passed including 6 new tests pinning the read-modify-write, the batching, idempotence, and both refusals.pre-commit run --all-filesgreen.Local note: this workstation's Python has no CA bundle, so the read path needs
SSL_CERT_FILE. That is the same local trust-store dead end #1344 wasted time on, not a defect here - the dev-base image's python3 carries certs.