Story-1203 — AgentCore networking: resolve networkMode: "PUBLIC", decide + spec VPC mode
pramaan-io/pramaan-spec#1203 · pramaan-agents · 2026-06-12 · parent EPIC-006
Decision. VPC mode is required for the Atlas runtime (and every future product agent) before it touches the shared Postgres cluster or any real privileged data — with no NAT gateway, so agents have zero internet egress, exactly as pramaan-docs ADR-070 demands.
networkMode: "PUBLIC" is tolerated only for the current state (staging Atlas, no DB, no privileged data, Bedrock-only) and is never copied to new runtimes.
Scope change honored, conflict surfaced. Founder direction (2026-06-12): agent-infra IaC moves from CDK to Terraform; the CDK clause of pramaan-docs ADR-108 is to be superseded. Therefore this PR ships the decision and a Terraform-ready parameter spec (
docs/notes/agentcore-networking-terraform-spec.md) instead of an agentcore.json/CDK change. Conflict noted rather than silently absorbed: pramaan-docs ADR-108 (accepted) still mandates the CDK path and this repo's AGENTS.md still lists Terraform-here as an anti-pattern — so a superseding ADR in pramaan-docs must land before any *.tf is written (in pramaan-infra per pramaan-docs ADR-067, never in this repo).
1. What PUBLIC vs VPC actually means (official AWS docs, fetched 2026-06-12)
Sources: AgentCore devguide agentcore-vpc.html, control-plane API reference (NetworkConfiguration, VpcConfig), Bedrock PrivateLink guide (vpc-interface-endpoints.html).
| PUBLIC today | VPC required before real data | |
|---|---|---|
| Inbound | Identical in both modes: the SigV4 InvokeAgentRuntime AWS API, called by the pramaan-functions broker. The runtime never exposes a public listener — network mode is purely an egress question. | |
| Egress | Container runs in an AWS-managed service network with open internet egress — a standing ADR-070 violation, and it cannot reach the private Postgres. | ENIs in our subnets via service-linked role AWSServiceRoleForBedrockAgentCoreNetwork. AWS: “When you connect AgentCore Runtime to a VPC, it does not have internet access by default.” Public subnets don't change that — ENIs get private IPs only; internet would need NAT, which we deliberately do not provision. |
| Config | none | networkModeConfig: subnets (1–16, required) + securityGroups (1–16, required); subnets must be in supported AZ IDs (ap-south-1: aps1-az1/az2/az3). |
Two facts that shaped the spec:
- We must provision our own S3 gateway endpoint. Per
VpcConfig.requireServiceS3Endpoint, runtimes created on/after the 2026-05-05 rollout get no service-managed S3 gateway — and AgentCore downloads CodeZip artifacts from service-ownedacr-code-*buckets at startup. The Atlas runtime was created 2026-06-11 (post-rollout): without our endpoint, the agent fails to start in VPC mode. - Bedrock survives no-internet VPC mode via PrivateLink.
com.amazonaws.ap-south-1.bedrock-runtimeserves Converse/InvokeModel; with private DNS enabled, boto3/strands need zero code changes. CloudWatch Logs needscom.amazonaws.ap-south-1.logs. ECR endpoints are not needed (Atlas is CodeZip, no image pull).
2. Account inventory (read-only AWS, profile default, ap-south-1, 2026-06-12)
| Resource | Found |
|---|---|
| VPC | vpc-06b42e2cb2e9f2936 — default VPC tagged stg-pramaan-vpc, CIDR 172.31.0.0/16 |
| Subnets | subnet-0aff595f086a3c26c (aps1-az1), subnet-05c5a5875be47b344 (aps1-az2), subnet-0c0ca0f2c4b27c7c6 (aps1-az3) — all in supported AZ IDs; all “public”, which is harmless: agent ENIs carry private IPs and there is no NAT, so VPC mode here yields zero internet egress |
| NAT gateways | none — and we keep it that way for agents |
| VPC endpoints | only SES (….email); bedrock-runtime, logs, and the S3 gateway must all be created |
| Shared Postgres | RDS pramaan-dev-db — same VPC, not publicly accessible, pramaan-dev-db.cjsgoq2287fa.ap-south-1.rds.amazonaws.com:5432, subnet group spans all 3 subnets, SGs sg-0f42f9153947e2ebf, sg-04db969701da47672 |
Everything VPC mode needs already exists or is a small additive change — no new VPC, no re-homing of the database.
3. Before / after
| Artifact | Before | After this PR |
|---|---|---|
agentcore/agentcore.json | "networkMode": "PUBLIC" | Unchanged by design (scope change: no CDK-path wiring). Flips to VPC with the Terraform migration; target config recorded in the spec: networkMode: "VPC" + the 3 subnets + the new agent SG. |
agentcore/AGENTS.md | “⚠️ OPEN: networkMode: "PUBLIC"… mandatory follow-up” | “DECIDED (Story-1203…): VPC mode required before Postgres/real data; no NAT ever; wiring spec in docs/notes/agentcore-networking-terraform-spec.md; lands via Terraform in pramaan-infra after the superseding ADR.” |
Root AGENTS.md | “OPEN FOLLOW-UP: … VPC-mode decision is a mandatory follow-up story” | “DECIDED (Story-1203)…” pointing at the spec. |
| Spec | — | docs/notes/agentcore-networking-terraform-spec.md — decision, doc evidence, inventory, and the full Terraform parameter set. |
4. The Terraform-ready parameters (summary — full detail in the spec)
- Agent SG
agentcore-atlas-runtime-stg: no inbound; egress 443 → VPC-endpoints SG, 443 → S3 prefix list, 5432 → RDS SGsg-0f42f9153947e2ebf. - VPC-endpoints SG
agentcore-vpce-stg: inbound 443 from the agent SG. - Interface endpoints (private DNS on, all 3 subnets):
bedrock-runtime,logs; optional later:monitoring,xray. - S3 gateway endpoint on the main route table, policy scoped to
arn:aws:s3:::acr-code-*-ap-south-1-an(+/*) — required for CodeZip startup. - RDS SG rule: inbound 5432 from the agent SG.
- Runtime update:
networkMode: "VPC"with the 3 subnet IDs + the agent SG. Deliberately no NAT variable anywhere — adding agent internet needs a new ADR.
{
"networkMode": "VPC",
"networkModeConfig": {
"subnets": [
"subnet-0aff595f086a3c26c",
"subnet-05c5a5875be47b344",
"subnet-0c0ca0f2c4b27c7c6"
],
"securityGroups": ["<agentcore-atlas-runtime-stg id>"]
}
}
5. What remains (outside this Story)
- Superseding ADR in pramaan-docs (Terraform replaces the ADR-108 CDK clause for agent infra) — blocks all
*.tfwork. - pramaan-infra PR implementing §4, then flipping the runtime to VPC and redeploying staging. Acceptance: Bedrock answers via PrivateLink;
pramaan-dev-db:5432reachable; public internet unreachable from the agent. - Reconcile
agentcore/agentcore.json(still says PUBLIC) with deployed reality during the migration. - Optional hardening: dedicated private subnets for agent ENIs;
monitoring/xrayendpoints with ADR-104 convergence.
Verification performed: official AWS docs fetched (not guessed); AWS account access read-only throughout; no agentcore deploy executed; rails (src/) and Atlas behavior (app/atlas/) untouched.