The provider path allocates a VpcPrefix by selecting an IPBlock and requesting a prefix length. Tenant-managed roots need the tenant to select the exact CIDR they want, and the same CIDR must be allocatable in two separate VPCs without colliding in REST cloud-IPAM.
Current state
rest-api/api/pkg/api/handler/vpcprefix.go, the VpcPrefix workflows, and inventory cleanup implement the existing ipBlockId + prefixLength path.
- REST cloud-IPAM derives a shared namespace from routing type/provider/site.
rest-api/ipam/prefix.go::AcquireSpecificChildPrefix already handles an exact strict child.
- An exact child equal to its parent needs explicit lease representation rather than changing
IPBlock.FullGrant semantics.
General idea
- Preserve legacy
ipBlockId + prefixLength requests and provider/Allocation namespaces.
- Model legacy
ipBlockId + prefixLength and tenant sitePrefixId + prefix as a strict OpenAPI oneOf, not one request with a bag of independently nullable fields. Derive prefix length from the canonical tenant CIDR and keep the internal IPBlock projection out of the public contract.
- Add the VpcPrefix allocation-mode, persisted namespace identity, and stored lease fields in this issue's migration.
- Materialize a tenant IPAM namespace keyed by VPC and SitePrefix identity, then install the tenant root as that namespace's parent.
- Use
AcquireSpecificChildPrefix for strict children. Represent an exact full-root lease as an explicit VpcPrefix allocation mode with correct release/utilization behavior.
- For exact mode, use the active
(vpc_id, site_prefix_id, canonical_prefix) lease/resource key as the retry identity. Persist/reuse the VpcPrefix row, namespace, mode, and acquired lease before Core invocation; a retry loads those records and never reruns allocation.
- Pass the exact Core SitePrefix ID in the Core VpcPrefix create request. A definitive Core rejection, including same-VPC overlap, compensates the newly acquired lease exactly once and records the terminal REST state; an unknown timeout retains the lease and resumes with the same Core ID.
- Keep the Core SitePrefix ID optional for legacy provider requests during mixed versions until configured-root adoption is complete and the tenant gate is enabled.
- Return
sitePrefixId and allocation mode on tenant VpcPrefixes, omit the internal projection IPBlock ID from that public mode, and add a sitePrefixId list filter so a tenant can find every carving from one root. Do not present root-global "available CIDRs" because capacity is scoped per VPC namespace.
- Let the existing REST VpcPeering path surface Core's overlap rejection; do not add a second REST evaluator.
Acceptance criteria
- Existing
ipBlockId + prefixLength requests and namespaces behave as before.
- Exactly one OpenAPI request variant is accepted. Tenant exact mode requires a
Ready SitePrefix owned by the path tenant/site; mixed automatic/exact fields are rejected.
- An exact strict child and the exact full root, including the ratified
/31 case, can be leased and released.
- The same exact CIDR can be leased into two eligible VPC namespaces; overlap in one VPC is rejected.
- Retry after crash or unknown Core timeout reuses one VpcPrefix row, namespace, mode, lease, and Core ID.
- Definitive Core rejection compensates only the lease created by that request and cannot release another VPC's equal CIDR.
- A
Deleting, wrong-tenant, wrong-site, provider, or Legacy root cannot use tenant exact mode.
- Capability-off blocks new exact leases while existing tenant VpcPrefixes remain listable and safely releasable.
- Old/new REST/Core combinations preserve legacy provider allocation while the tenant capability is off.
- Compensation, deletion, inventory cleanup, privacy, OpenAPI/SDK, and both allocation-mode tests are included.
Not in this issue
- Changing all provider namespaces.
- Direct tenant SitePrefix-to-Subnet allocation.
- Tenant IPv6.
- A REST peering lifecycle rewrite.
Likely areas
rest-api/api/pkg/api/handler/vpcprefix.go and model/tests
rest-api/db/pkg/db/model/vpcprefix.go and a focused migration
rest-api/db/pkg/db/ipam
rest-api/ipam/prefix.go
rest-api/site-workflow/pkg/activity/vpcprefix.go
rest-api/workflow/pkg/activity/vpcprefix and workflow package
rest-api/openapi/spec.yaml and generated clients
Planning
- Size: L
- Risk: Critical
- Best fit: Senior Go/IPAM engineer with Core reviewer
Dependencies
Part of #3883.
The provider path allocates a VpcPrefix by selecting an IPBlock and requesting a prefix length. Tenant-managed roots need the tenant to select the exact CIDR they want, and the same CIDR must be allocatable in two separate VPCs without colliding in REST cloud-IPAM.
Current state
rest-api/api/pkg/api/handler/vpcprefix.go, the VpcPrefix workflows, and inventory cleanup implement the existingipBlockId + prefixLengthpath.rest-api/ipam/prefix.go::AcquireSpecificChildPrefixalready handles an exact strict child.IPBlock.FullGrantsemantics.General idea
ipBlockId + prefixLengthrequests and provider/Allocation namespaces.ipBlockId + prefixLengthand tenantsitePrefixId + prefixas a strict OpenAPIoneOf, not one request with a bag of independently nullable fields. Derive prefix length from the canonical tenant CIDR and keep the internal IPBlock projection out of the public contract.AcquireSpecificChildPrefixfor strict children. Represent an exact full-root lease as an explicit VpcPrefix allocation mode with correct release/utilization behavior.(vpc_id, site_prefix_id, canonical_prefix)lease/resource key as the retry identity. Persist/reuse the VpcPrefix row, namespace, mode, and acquired lease before Core invocation; a retry loads those records and never reruns allocation.sitePrefixIdand allocation mode on tenant VpcPrefixes, omit the internal projection IPBlock ID from that public mode, and add asitePrefixIdlist filter so a tenant can find every carving from one root. Do not present root-global "available CIDRs" because capacity is scoped per VPC namespace.Acceptance criteria
ipBlockId + prefixLengthrequests and namespaces behave as before.ReadySitePrefix owned by the path tenant/site; mixed automatic/exact fields are rejected./31case, can be leased and released.Deleting, wrong-tenant, wrong-site, provider, or Legacy root cannot use tenant exact mode.Not in this issue
Likely areas
rest-api/api/pkg/api/handler/vpcprefix.goand model/testsrest-api/db/pkg/db/model/vpcprefix.goand a focused migrationrest-api/db/pkg/db/ipamrest-api/ipam/prefix.gorest-api/site-workflow/pkg/activity/vpcprefix.gorest-api/workflow/pkg/activity/vpcprefixand workflow packagerest-api/openapi/spec.yamland generated clientsPlanning
Dependencies
Part of #3883.