deploy: private deploys with allowed_ips ingress whitelist (Pro+ feature)#45
Merged
Merged
Conversation
…ure) POST /deploy/new now accepts two new multipart fields: - private (bool-ish: "true" / "1" / "yes") — when true the resulting Ingress carries an nginx.ingress.kubernetes.io/whitelist-source-range annotation built from allowed_ips. - allowed_ips — comma-separated CIDRs / IPs. Each entry is validated via net.ParseCIDR / net.ParseIP; max 32 entries (larger lists belong in CF Access / a real VPN, not an nginx annotation). Tier gate: Pro / Pro-yearly / Team / Team-yearly / Growth only. Hobby / anonymous / free / yearly-free → 402 with the new AgentActionPrivateDeployRequiresPro constant pointing at https://instanode.dev/pricing. Validation order is tier-gate FIRST so low-tier callers never see the "missing allowed_ips" 400 — they only see the upgrade wall, which is the only action that matters to them. Migration 020_deployment_access_control.sql adds two columns to deployments: private BOOLEAN NOT NULL DEFAULT false and allowed_ips TEXT NOT NULL DEFAULT ''. Stored as a comma-joined string (not JSONB array) because the nginx annotation already requires that exact form — round-trip is lossless and the existing scanDeployment code path keeps its scalar-friendly shape. Compute layer: - compute.DeployOptions gets Private bool + AllowedIPs []string. - K8sProvider.applyIngressForDeploy now sets the annotation when private is true and the slice is non-empty (belt-and-suspenders against a stray "allow nobody" Ingress). OpenAPI 3.1 spec extended with the two request fields, the two response fields, the 402 wall, and updated 400-error list. Two new agent_action constants: - AgentActionPrivateDeployRequiresPro (402 wall) - AgentActionPrivateDeployRequiresAllowedIPs (400 wall for private=true with empty allowed_ips) Both pass TestAgentActionContract automatically — added to the contract case table. Tests: 7 new cases in deploy_private_test.go (Pro accept, Hobby 402, empty IPs 400, invalid IP 400, 33-IP cap, public default unchanged, GET /api/v1/deployments round-trip). All `make test-unit` packages green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
mastermanas805
added a commit
that referenced
this pull request
May 21, 2026
OSV-Scanner flags 4 CVEs in prometheus/prometheus v0.303.0 (the server-binary module, pulled in transitively by OTel/Grafana consumers) but govulncheck confirms 0 reachable on the current api master. Mirrors worker PR #45 (same suppression with same rationale). Per CLAUDE.md rule 25: explicit reason + exit condition documented. Suppressions auto-lift when upstream consumers bump past v0.303.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /deploy/new accepts two new multipart fields gating ingress access:
nginx.ingress.kubernetes.io/whitelist-source-rangebuilt fromallowed_ips.net.ParseCIDR/net.ParseIP; max 32 entries.Tier gate: Pro / Pro-yearly / Team / Team-yearly / Growth only.
Hobby / anonymous / free → 402 with
private_deploy_requires_proand thenew
AgentActionPrivateDeployRequiresProconstant pointing athttps://instanode.dev/pricing.Tier-gate runs before the allowed_ips validation so low-tier callers
see only the upgrade wall — the action that matters to them.
Changes
020_deployment_access_control.sql— addsprivate BOOLEANand
allowed_ips TEXTto deployments (defaults preserve existingbyte-identical behaviour for public deploys).
internal/handlers/deploy.go— parses + validates new fields.internal/handlers/deploy_private.go— new helper file isolating thewhole rule-set in one place for U3 audits.
internal/handlers/agent_action.go— two new constants(
AgentActionPrivateDeployRequiresPro,AgentActionPrivateDeployRequiresAllowedIPs), both auto-passingTestAgentActionContract.internal/providers/compute/provider.go— extendsDeployOptionswithPrivate bool+AllowedIPs []string.internal/providers/compute/k8s/client.go—applyIngressForDeploysets the nginx annotation when private && len(IPs) > 0.
internal/models/deployment.go— extendsDeploymentandCreateDeploymentParamswith the new fields; comma-join helpers.internal/handlers/openapi.go— DeployRequest, DeployResponse, 402error documented.
Test plan
make test-unitgreen across every package (handlers, models, k8s,middleware, plans, providers, quota, router, urls).
deploy_private_test.go— all pass:private_deploy_requires_allowed_ips)too_many_allowed_ips)private+allowed_ipsTestAgentActionContractpasses — both new constants registered.TestOpenAPIpasses — JSON spec is still valid.build/deploy/E2E.
🤖 Generated with Claude Code