Skip to content

deploy: PATCH /api/v1/deployments/:id to edit private + allowed_ips in-place#46

Merged
mastermanas805 merged 1 commit into
masterfrom
feat/patch-deployment-access-fresh
May 13, 2026
Merged

deploy: PATCH /api/v1/deployments/:id to edit private + allowed_ips in-place#46
mastermanas805 merged 1 commit into
masterfrom
feat/patch-deployment-access-fresh

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

Semantics

  • allowed_ips on PATCH REPLACES (not appends). Documented inline in patchAccessControlBody. Matches REST conventions for collection fields and matches what PrivacyPanel renders/submits. Append semantics would silently grow the allow-list across multiple PATCHes.
  • private: false clears the allow-list regardless of allowed_ips in the same body — preserves the "public deploy has no whitelist annotation" invariant.
  • Sending only allowed_ips keeps the current private flag.
  • Empty body returns 400 missing_fields (not a silent no-op).

Test plan

  • make test-unit green (all packages, ~24s on handlers)
  • 8 new tests in deploy_private_patch_test.go all pass:
    • Pro flips public→private with valid IPs (200, row persisted via GET round-trip)
    • PATCH with only allowed_ips REPLACES the existing list (explicit anti-append assertion)
    • private: false clears the allow-list to empty []
    • Hobby tier hits 402 with reused AgentActionPrivateDeployRequiresPro
    • Invalid IP/CIDR surfaces verbatim in 400 message
    • 404 on missing deploy
    • 403 on cross-team
    • Empty body returns 400 missing_fields
  • TestAgentActionContract still passes — no new agent-action strings added
  • Existing 7 TestDeployNew_Private_* tests still pass (validation refactor preserves behaviour)

🤖 Generated with Claude Code

…n-place

Dashboard PrivacyPanel (PR #44) needed a way to edit access-control on an
existing deploy. POST /deploy/new with private/allowed_ips shipped in PR #45
but no PATCH surface — every PrivacyPanel save hit 404.

What lands:

- PATCH /api/v1/deployments/:id accepts {private?, allowed_ips?} JSON. Body
  fields are optional pointers so "omit" and "set to zero" are distinguishable.
  Sending only allowed_ips preserves the current private state; sending
  private:false clears the allow-list regardless of allowed_ips in the same
  body (preserves the public-deploy invariant).

- Validation reuses PR #45's parsePrivateDeployFields rule-set by factoring
  out validatePrivateDeployFields — the tier gate, non-empty IPs check,
  cap, and per-entry parse are shared with POST. The U3 reviewer audits the
  rules in one place.

- compute.Provider gains UpdateAccessControl(ctx, appID, private, allowedIPs).
  K8s implementation Get/Update's the existing Ingress and rewrites the
  whitelist-source-range annotation via a new shared helper
  buildIngressAccessAnnotations — same function the create path now uses,
  so create and update can't drift on the annotation key. Noop logs+returns
  nil. Local-dev (no DEPLOY_DOMAIN) is a no-op with a warn breadcrumb.

- models.UpdateDeploymentAccessControl writes the private + allowed_ips
  columns. Single-row update — no caching/aggregation concerns.

- Semantics decision documented inline: allowed_ips on PATCH REPLACES the
  current list (not appends). Matches REST collection-field conventions and
  is what PrivacyPanel renders/submits.

Tests: 8 new in deploy_private_patch_test.go, all pass against the noop
compute provider. Pro flips public→private, replace semantics, private:false
clears the list, hobby 402 with reused AgentActionPrivateDeployRequiresPro,
invalid IP surfaces verbatim, 404 / 403 / empty-body 400. TestAgentActionContract
still passes (no new strings added).

make test-unit: all packages green (handlers 23.6s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 0e02996 into master May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant