Skip to content

fix(api): deploy_ttl anon walls emit claim_required, not upgrade_required (B7-P1-7)#206

Merged
mastermanas805 merged 2 commits into
masterfrom
fix/api-deploy-ttl-claim-required-2026-05-30
May 30, 2026
Merged

fix(api): deploy_ttl anon walls emit claim_required, not upgrade_required (B7-P1-7)#206
mastermanas805 merged 2 commits into
masterfrom
fix/api-deploy-ttl-claim-required-2026-05-30

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

Summary

POST /api/v1/deployments/:id/{make-permanent,ttl} returned 402 with
error: "upgrade_required" for anonymous-tier callers. The remediation
is a free claim, not a paid upgrade — so the keyword was wrong.
Agents that branch on the error code (instead of reading the prose
agent_action) routed users to https://instanode.dev/pricing
(paid checkout) when a 30-second free claim would have cleared the wall.

Closes BugBash 2026-05-20 finding B7-P1-7.

Wire shape preserved (status 402, message, agent_action sentence), only
the machine-readable error keyword + upgrade_url change:

  • error: upgrade_required -> claim_required
  • upgrade_url: https://api.instanode.dev/start (302 alias) -> https://instanode.dev/claim (canonical)

Rule 17 coverage block

Symptom:        JSON body `error: "upgrade_required"` on anon /make-permanent + /ttl
Enumeration:    rg -F '"upgrade_required"' internal/handlers/deploy_ttl.go
Sites found:    2  (deploy_ttl.go:65, deploy_ttl.go:139)
Sites touched:  2  (both flipped to "claim_required")
Coverage test:  TestDeployTTL_AnonymousArmsEmitClaimRequired iterates a
                route table (make-permanent + set-ttl). A third arm that
                emits upgrade_required fails the matching row.
                + TestDeployTTL_NoUpgradeRequiredInSource source-grep gate.
Live verified:  Pending - awaiting prod deploy + healthz commit_id gate
                (rule 14).

Rule 22 surface check

Contract changes touched in this single PR:

  • internal/handlers/deploy_ttl.go (both anon walls)
  • internal/handlers/helpers.go (codeToAgentAction["claim_required"] registered with UpgradeURL: https://instanode.dev/claim and a claim-not-upgrade agent_action)
  • internal/handlers/openapi.go (402 description on both routes)
  • openapi.snapshot.json (regenerated via make openapi-snapshot - cross-stack contract gate)
  • internal/handlers/agent_action_contract_test.go (expectedCodes registry now lists claim_required so a future drop is a contract regression)

Other handlers that legitimately use upgrade_required for paid-plan walls (db.go, vector.go, nosql.go, queue.go, cache.go, backup.go, stack.go, resource.go, etc.) are untouched.

Test plan

  • go test ./internal/handlers/ -run 'TestDeployTTL_AnonymousArmsEmitClaimRequired|TestDeployTTL_NoUpgradeRequiredInSource' -count=1 -short -> ok
  • go test ./internal/handlers/ -run 'TestAgentActionContract|TestMakePermanent|TestSetTTL|TestDeployTTL|TestCodeToAgentAction' -count=1 -short -> ok
  • go build ./... clean
  • go vet ./internal/handlers/ clean
  • make openapi-snapshot regenerated; diff = exactly 2 line changes (the two 402 descriptions)
  • CI green
  • Post-deploy live verify (rule 14): curl https://api.instanode.dev/api/v1/deployments//make-permanent and assert error: "claim_required" + upgrade_url: "https://instanode.dev/claim"

mastermanas805 and others added 2 commits May 30, 2026 21:30
…ired (B7-P1-7)

POST /api/v1/deployments/:id/{make-permanent,ttl} returned 402 with
`error: "upgrade_required"` on anonymous-tier callers. The agent_action
sentence correctly said "claim the account (free)", but agents that
branch on the `error` keyword route on `upgrade_required` to the paid
pricing page (codeToAgentAction["upgrade_required"].UpgradeURL =
https://instanode.dev/pricing). The actual remediation is a FREE claim,
not a paid upgrade — wrong destination for the strict code-switching
caller.

Rule 17 coverage block:
  Symptom:        JSON body `error: "upgrade_required"` on anon /make-permanent + /ttl
  Enumeration:    rg -F '"upgrade_required"' internal/handlers/deploy_ttl.go
  Sites found:    2  (deploy_ttl.go:65, deploy_ttl.go:139)
  Sites touched:  2  (both flipped to "claim_required")
  Coverage test:  TestDeployTTL_AnonymousArmsEmitClaimRequired (iterates a
                  route table — a third arm that emits upgrade_required
                  fails the matching row) + TestDeployTTL_NoUpgradeRequiredInSource
                  (source-grep belt+braces)
  Live verified:  pending — awaiting prod deploy + healthz commit_id gate

Rule 22 surface check (contract change touches all surfaces in one PR):
  - internal/handlers/deploy_ttl.go (both anon walls)
  - internal/handlers/helpers.go (codeToAgentAction["claim_required"]
    registered with UpgradeURL: https://instanode.dev/claim)
  - internal/handlers/openapi.go (402 description on both routes)
  - openapi.snapshot.json (regenerated via `make openapi-snapshot`)
  - internal/handlers/agent_action_contract_test.go (expectedCodes
    registry — claim_required is now a guaranteed-present entry)

Wire shape preserved:
  - Status: 402 (unchanged)
  - agent_action: still points at https://instanode.dev/claim with the
    same "Claim the account" copy (AgentActionDeployMakePermanentAnonymous)
  - upgrade_url: changes from https://api.instanode.dev/start (a 302 to
    /claim) to https://instanode.dev/claim directly — fewer hops for
    the calling agent

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 85cbba8 into master May 30, 2026
16 checks passed
@mastermanas805 mastermanas805 deleted the fix/api-deploy-ttl-claim-required-2026-05-30 branch May 30, 2026 16:23
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