feat(aosguard): let the netlify alias leaf remove, so a rename is one write #1358

Merged
coilyco-ops merged 2 commits from aos/claude/mt75-netlify-remove into main 2026-08-28 21:08:40 +00:00
Owner

The surface I shipped in #1355 was one-way: aliases could accumulate and never be shed. Vera hit it finishing a rename Kai asked for.

Current live state

Confirmed by reading the site, not from the report:

custom_domain: www.coilysiren.me
  umbra.coilyco.ai
  agent-compose.coilyco.ai   <- renamed away
  mcp-beaver.coilyco.ai      <- renamed away
  beaver.coilyco.ai
  acompose.coilyco.ai

Five aliases where there should be three. The two dead names hold valid certificates and will serve the site root once Delphi's rules move. Nothing is broken; it is untidy and irreversible.

Worth recording: the read-modify-write did its job on the first write where it mattered. Vera's add reported "adding 2 alias(es) to 3 existing" and preserved all five.

Why --remove on the same leaf, not an alias remove verb

A rename is a removal and an addition of the same thing. Two verbs make it two writes, and every write re-issues the certificate covering www.coilysiren.me. That is exactly the cost the batched add was built to avoid, reappearing one operation over.

Why not alias set taking the final list, which Vera raised and held loosely: set moves the read-modify-write to the caller, and a caller stating what exists rather than what changes is the precise failure that bit twice today - her kai-server ConfigMap comment showing what changed rather than what the object held, and this field's own replace semantics. set would be honest about the API and hand the trap back to the caller. add --remove keeps the union logic inside the module. Her lean and mine agree.

The three decisions she asked for

  • Removing an absent alias is a named refusal, not a silent success. A mistyped domain is the error most likely to go unnoticed on this surface, and a silent success is how it would go unnoticed.
  • Removing the primary domain is refused, matching the existing refusal on adding it. custom_domain is a separate field so it could not be clobbered structurally, but a caller asking has misunderstood and should be told.
  • Removing every alias is allowed, deliberately rather than incidentally. Refusing it would leave the surface one-way at the end, which is the defect this PR exists to close.

Also refused: the same name passed to both --alias and --remove.

Verified against the built binary and live state

call result
neither flag refused
--remove an absent alias refused, names the alias
--remove the primary domain refused
same name added and removed refused
no --site still denied at the guard

Tests pin the rename as a single write with the untouched alias preserved, plus each refusal and the remove-everything case. uv run pytest 753 passed, pre-commit run --all-files green.

Finishing the rename

Once this lands and aosguard is rebuilt, one call removes both dead names:

aosguard ops netlify alias add --site coilysiren-dot-me.netlify.app --remove agent-compose.coilyco.ai --remove mcp-beaver.coilyco.ai

One write, one certificate event. That is Vera's to run, and it wants watching for the same reason the add did.

The surface I shipped in #1355 was one-way: aliases could accumulate and never be shed. Vera hit it finishing a rename Kai asked for. ## Current live state Confirmed by reading the site, not from the report: ``` custom_domain: www.coilysiren.me umbra.coilyco.ai agent-compose.coilyco.ai <- renamed away mcp-beaver.coilyco.ai <- renamed away beaver.coilyco.ai acompose.coilyco.ai ``` Five aliases where there should be three. The two dead names hold valid certificates and will serve the site root once Delphi's rules move. Nothing is broken; it is untidy and irreversible. Worth recording: the read-modify-write did its job on the first write where it mattered. Vera's add reported "adding 2 alias(es) to 3 existing" and preserved all five. ## Why `--remove` on the same leaf, not an `alias remove` verb **A rename is a removal and an addition of the same thing.** Two verbs make it two writes, and every write re-issues the certificate covering `www.coilysiren.me`. That is exactly the cost the batched add was built to avoid, reappearing one operation over. **Why not `alias set` taking the final list**, which Vera raised and held loosely: `set` moves the read-modify-write to the caller, and *a caller stating what exists rather than what changes* is the precise failure that bit twice today - her kai-server ConfigMap comment showing what changed rather than what the object held, and this field's own replace semantics. `set` would be honest about the API and hand the trap back to the caller. `add --remove` keeps the union logic inside the module. Her lean and mine agree. ## The three decisions she asked for * **Removing an absent alias is a named refusal**, not a silent success. A mistyped domain is the error most likely to go unnoticed on this surface, and a silent success is how it would go unnoticed. * **Removing the primary domain is refused**, matching the existing refusal on adding it. `custom_domain` is a separate field so it could not be clobbered structurally, but a caller asking has misunderstood and should be told. * **Removing every alias is allowed**, deliberately rather than incidentally. Refusing it would leave the surface one-way at the end, which is the defect this PR exists to close. Also refused: the same name passed to both `--alias` and `--remove`. ## Verified against the built binary and live state | call | result | |---|---| | neither flag | refused | | `--remove` an absent alias | refused, names the alias | | `--remove` the primary domain | refused | | same name added and removed | refused | | no `--site` | still denied at the guard | Tests pin the rename as a **single** write with the untouched alias preserved, plus each refusal and the remove-everything case. `uv run pytest` 753 passed, `pre-commit run --all-files` green. ## Finishing the rename Once this lands and aosguard is rebuilt, one call removes both dead names: `aosguard ops netlify alias add --site coilysiren-dot-me.netlify.app --remove agent-compose.coilyco.ai --remove mcp-beaver.coilyco.ai` One write, one certificate event. That is Vera's to run, and it wants watching for the same reason the add did.
feat(aosguard): let the netlify alias leaf remove, so a rename is one write
All checks were successful
ci / aos-eval-tests (pull_request) Successful in 11s
ci / aos-cli-tests (pull_request) Successful in 40s
ci / gate (pull_request) Successful in 1m12s
490924aee2
The surface was one-way: aliases could accumulate and never be shed. Vera hit
it finishing a rename Kai asked for, and the site now carries five aliases
where it should carry three, with two dead names holding valid certificates.

`--remove` joins `--alias` on the same leaf rather than arriving as its own
verb. A rename is a removal and an addition of the same thing, and each write
re-issues the certificate covering www.coilysiren.me, so two verbs would make
a rename two certificate events. That is the cost the batched add was built
to avoid, reappearing one operation over.

`add --remove` over an `alias set` taking the final list, on Vera's reasoning
and mine: `set` moves the read-modify-write to the caller, and a caller
stating what exists rather than what changes is the exact failure that bit
twice today, in the kai-server ConfigMap comment and in this field itself.
The union logic stays inside the module.

Removing an absent alias is a named refusal rather than a silent success. A
mistyped domain is the error most likely to go unnoticed here, and a silent
success is how it would go unnoticed.

Removing the primary domain is refused the way adding it already was.
Removing every alias is allowed, deliberately: the surface would otherwise
still be one-way at the end.

Verified against the built binary and live state: all four refusals fire, and
the site guard still holds. Tests pin the rename as a single write.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
Merge branch 'main' into aos/claude/mt75-netlify-remove
All checks were successful
ci / aos-cli-tests (pull_request) Successful in 48s
ci / aos-eval-tests (pull_request) Successful in 10s
ci / gate (pull_request) Successful in 56s
f15fca7ef5
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1358
No description provided.