The agent tier's route tables and the lockbox tier's route tables both include a route to the DynamoDB gateway endpoint. So both tiers can reach DynamoDB.
Will they share a data store? How is the isolation actually enforced?
They do not share tables. Different tables, different encryption keys, different IAM policies. Network reachability to DynamoDB is shared, but four independent layers above the network ensure that an agent calling the lockbox table gets AccessDenied — and for the same call to succeed, all four layers would have to be simultaneously misconfigured.
| Tier | Table(s) | Purpose | Encrypted with |
|---|---|---|---|
| Lockbox | ai-sandbox-lockbox-tokens |
Token → PII map. Holds actual sensitive customer data (Customer / Branch / Contact PII) as ciphertext. | Lockbox CMK alias/ai-sandbox-lockbox |
| Agent | ai-sandbox-agent-state (and per-agent state tables) |
Agent conversation state, scratch references, opaque tokens. Never PII. | Agent CMK alias/ai-sandbox-agent |
So even before any of the four enforcement layers fire, an agent calling DDB cannot accidentally read the lockbox table by name — it's a different resource ARN. The four layers exist to make accessing it impossible even if the ARN is known and explicitly targeted.
Reading top-down, each layer is independent of the others. A misconfiguration in one is caught by the next.
The DynamoDB gateway endpoint has a resource policy. The policy is designed (in Step 4a, the Data module) to scope which subnets may access which tables. This is the lowest level of enforcement — it happens at the VPC endpoint itself, before the DDB API even receives the request.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AgentSubnetsAccessAgentTablesOnly",
"Effect": "Allow",
"Principal": "*",
"Action": [
"dynamodb:GetItem", "dynamodb:PutItem",
"dynamodb:UpdateItem", "dynamodb:DeleteItem",
"dynamodb:Query", "dynamodb:Scan"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:959228203854:table/ai-sandbox-agent-*"
],
"Condition": {
"StringEquals": {
"aws:SourceVpc": "vpc-0ed3a16a8b82f7a34"
},
"StringLike": {
"aws:PrincipalTag/tier": ["sor-reader", "internal"]
}
}
},
{
"Sid": "LockboxRoleAccessesLockboxTables",
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": [
"arn:aws:dynamodb:us-east-1:959228203854:table/ai-sandbox-lockbox-*"
],
"Condition": {
"ArnEquals": {
"aws:PrincipalArn": "arn:aws:iam::959228203854:role/ai-sandbox-lockbox-service"
}
}
}
]
}
Effect: an agent workload trying to query the lockbox table gets AccessDenied at the VPC endpoint, before the DDB API is ever consulted. The DDB API has no idea the request was attempted.
Note: this policy ships with the Data module in Step 4a. The DDB gateway endpoint is currently deployed without a custom policy (Step 3); the default policy allows all access. Layer 1 will be locked down when Step 4a applies.
The tier roles deployed in Step 2 have explicit boolean flags expressing what each tier may do:
| Tier role | can_read_lockbox_tokens | can_call_lockbox_reveal | Effective DDB access on lockbox tables |
|---|---|---|---|
sor-reader |
true | false | Can GetItem on the lockbox table (gets back encrypted ciphertext); cannot detokenize. |
internal |
false | false | Cannot touch the lockbox table at all (IAM policy does not include the resource). |
| Lockbox service role (Step 4a) | n/a | (it is the reveal Lambda's role) | Full read/write on lockbox table. This is the only role with that capability. |
Effect: even if Layer 1 (endpoint policy) was leaking, an internal-tier agent calling GetItem on the lockbox table gets AccessDenied from IAM.
Verified live in Step 2. The lockbox CMK's key policy contains exactly two principal entries:
"Statement": [
{
"Sid": "EnableRootAccountAdmin",
"Effect": "Allow",
"Principal": "arn:aws:iam::959228203854:root"
},
{
"Sid": "AllowLockboxStorageServices",
"Effect": "Allow",
"Principal": [
"s3.amazonaws.com",
"dynamodb.amazonaws.com"
]
}
]
No agent tier role appears anywhere in this policy.
Effect: even if both Layer 1 and Layer 2 leaked, an agent receiving encrypted lockbox ciphertext cannot decrypt it. The agent's kms:Decrypt call returns AccessDenied at the cryptographic operation, not at the IAM API. The KMS layer is the deepest possible policy enforcement in AWS — it happens at the actual crypto operation, regardless of any IAM consideration above it.
This is the property called “structural PII-blindness of agents” in the project doctrine. It was the centerpiece verification of Step 2.
Layer 4 exists for the case where an agent legitimately needs the detokenized PII. The pattern: agent never decrypts directly; it asks the reveal Lambda to do it, and the reveal Lambda enforces the access policy.
Agent (has token "tok_abc123")
│
│ HTTPS 443 → lockbox-sg
│ (lockbox-sg only accepts inbound from agent-sg)
▼
Reveal Lambda (running in lockbox tier, assumes lockbox service role)
│
├── Authorization check
│ • caller's IAM role must have can_call_lockbox_reveal=true
│ • reveal purpose must be in the allowed-reason list
│ • caller has not exceeded rate limit
│
├── Audit log entry (encrypted with audit CMK)
│ • caller role, token, timestamp, reason, IP
│ • this is the SecOps reveal trail
│
├── DDB GetItem on lockbox table
│ • lockbox service role has access (Layer 2 allows it for this role)
│ • lockbox CMK decrypts because lockbox service role is in key policy
│
└── return PII to caller
│
▼
Caller (now has PII in memory; SHOULD NOT log it; ephemeral use only)
Why this is necessary: there are legitimate operational reasons for an authorized workflow to need the underlying PII (sending an email to a customer, displaying details in a UI, etc.). Layer 4 is the controlled, audited, rate-limited path for that case. Layers 1-3 ensure it is the only path.
| Threat / failure mode | Caught at | How |
|---|---|---|
Agent tries to GetItem on ai-sandbox-lockbox-tokens directly |
Layer 1 | Gateway endpoint policy denies cross-tier table access. DDB API never receives the request. |
| Layer 1 misconfigured; agent reaches DDB API | Layer 2 | IAM tier role policy denies access to lockbox table resource. |
| Layer 1 and Layer 2 both misconfigured; agent receives encrypted item | Layer 3 | KMS lockbox key policy refuses agent role's kms:Decrypt call. Item remains ciphertext. |
| Agent uses the legitimate reveal path with elevated permissions | Layer 4 | Reveal Lambda authorization check (role, purpose, rate limit). Every reveal audit-logged. |
| Compromised lockbox service role itself (worst case) | Audit trail | Every reveal is logged with caller and purpose. Anomaly detection (Step 4b observability) catches unusual reveal patterns. |
For an agent to successfully exfiltrate lockbox PII, four independent configurations would have to be simultaneously wrong. None of these configurations is changed by a single edit; each lives in a different module (network, identity, identity, data) and each requires explicit operator action to relax.
| Layer | Status after Step 3 (current) | Status after Step 4a (Data module) |
|---|---|---|
| Layer 1 (endpoint policy) | Default permit-all policy on the DDB and S3 gateway endpoints | Scoped policy denying cross-tier table access |
| Layer 2 (IAM tier policies) | Tier role policies are live (Step 2). The lockbox service role does not exist yet. | Lockbox service role created in data module; only role with full lockbox table access |
| Layer 3 (KMS lockbox key policy) | Live and verified (Step 2). Agent roles excluded from lockbox CMK. | Unchanged. Still enforced. |
| Layer 4 (reveal Lambda) | Does not exist yet. | Deployed by data module with the authorization + audit pattern above. |
Layers 1 and 4 are described in this document for completeness, but the resources they protect (the lockbox table itself, the reveal Lambda) only land in Step 4a. Until then, there is nothing on the lockbox side of the boundary to leak. Layers 2 and 3 already exist and would enforce the boundary the moment a lockbox table appeared, even before Step 4a wires Layers 1 and 4.
Two of the four layers exist today (post-Step 3); two are deployed in Step 4a. The viewable artifacts mirror that split.
| Layer | Where to look | What confirms enforcement |
|---|---|---|
| Layer 2 · sor-reader tier policy | IAM → Roles → ai-sandbox-agent-sor-reader → Permissions tab → click the inline policy tier-sor-reader-permissions → JSON |
The ReadLockboxTokens Sid is present but its Action is read-only DDB operations (GetItem, BatchGetItem, Query). No PutItem, no DeleteItem, no UpdateItem. So even at the IAM layer, a sor-reader role can see the encrypted token but cannot modify the lockbox table. |
| Layer 2 · internal tier policy | IAM → Roles → ai-sandbox-agent-internal → Permissions tab → JSON |
No ReadLockboxTokens Sid appears at all. The internal tier role has no IAM-level access to the lockbox table. Compare side-by-side with sor-reader for the diff. |
| Layer 3 · Lockbox CMK key policy (the centerpiece) | KMS → Customer managed keys → ai-sandbox-lockbox → Key policy tab |
Two principal blocks: arn:aws:iam::959228203854:root (admin), and the storage services s3.amazonaws.com / dynamodb.amazonaws.com. No agent role appears. This is the structural PII-blindness guarantee, as data, enforced at the cryptographic operation layer. |
| Layer 3 · Agent CMK key policy (for contrast) | KMS → ai-sandbox-agent → Key policy tab |
Agent tier role principals do appear here. So an agent can decrypt with the agent CMK; reading the two key policies side-by-side makes the boundary visible. |
| Permission boundary on both tier roles | IAM → ai-sandbox-agent-sor-reader → Permissions tab → scroll to "Permissions boundary" |
Shows ai-sandbox-permission-boundary attached. Caps everything else above. |
| Layer | Where to look (after Step 4a) | What confirms enforcement |
|---|---|---|
| Layer 1 · DDB gateway endpoint policy | VPC → Endpoints → click the DDB gateway endpoint vpce-0675c4f8582974d54 → Policy tab |
Custom policy (replaces the current permit-all default) scoping which subnets can access which tables. Cross-tier table access denied at the network layer. |
| Layer 1 · S3 gateway endpoint policy | VPC → Endpoints → click the S3 gateway endpoint vpce-04ccfcc0152402128 → Policy tab |
Same pattern for S3 buckets. Agent subnets can reach agent buckets; lockbox subnets can reach lockbox buckets; cross-access denied. |
| Layer 4 · Reveal Lambda | Lambda → Functions → ai-sandbox-lockbox-reveal |
VPC config: attached to lockbox-private subnets. Permissions: assumes a lockbox service role with full lockbox table access. Environment: audit log group ARN, allowed-caller list, rate limit. Inbound: lockbox-sg allows agent-sg on 443 only. |
| Layer 4 · The reveal audit log | CloudWatch → Log groups → /aws/lambda/ai-sandbox-lockbox-reveal-audit |
Encrypted with audit CMK. Every reveal call generates one log entry: caller role, token, timestamp, reason, IP. Reviewable by SecOps. |
ReadLockboxTokens Sid. Note it's read-only.ReadLockbox. Find nothing. Compare."An agent appears to have read PII it should not have." The investigation walks the four layers backward: did the application gate fail (reveal Lambda logs)? did KMS allow the decrypt (CloudTrail KMS events)? did IAM allow the GetItem (CloudTrail DDB events)? did the endpoint policy allow the network path (VPC flow logs)? Each layer has a separate audit trail. A single misconfiguration is bounded by the next layer; the audit trail says where it stopped.
The pattern in this document — shared network reachability, independent access controls at multiple layers above — appears repeatedly in the AI-sandbox doctrine:
The general principle: reachability at one layer is acceptable as long as access at higher layers is properly scoped. Removing reachability (running two endpoints, two VPCs, two subnets) is more expensive, more complex, and provides only marginal additional security over a well-scoped multi-layer policy stack. The discipline is to ensure the layers above are actually scoped, not assumed to be.
Agents and lockbox can both reach DynamoDB. They cannot share a table, share encryption keys, share IAM permissions, or share decrypted PII. Four independent enforcement layers ensure that. Sharing the endpoint is a deliberate choice that lets the strongest layer (the endpoint policy itself) be the table-scoping mechanism. The doctrine is “reachability is not access.”