Hyperscaler interop
Push MAQPNA policies into AWS Bedrock AgentCore Policy, and pull AWS CloudTrail, OpenAI and Anthropic audit events into a ledger as evidence of what ran outside the gateway.
The MAQPNA gateway governs every call that passes through it. Some agent traffic does not: an agent
that calls Bedrock directly, AgentCore's own Code Interpreter, or an admin who changes API keys in a
provider console. The gateway refuses provider-hosted tools (reasons provider_hosted_tool and
provider_hosted_mcp), because they run at the provider, outside its egress, data loss prevention
(DLP) and audit checks. MAQPNA covers the rest from both sides:
- Push.
maqpna policy push --to agentcorewrites your MAQPNA policies into an AWS Bedrock AgentCore policy engine and keeps them reconciled. AgentCore then enforces the same decisions on traffic MAQPNA never sees. - Pull.
maqpna audit importreads the providers' own audit events (AWS CloudTrail, the OpenAI organization audit log, the Anthropic Compliance API) into a ledger as evidence. Events that show a provider-hosted tool in use are reported as unmediated.
flowchart LR
P["MAQPNA policies<br/>ToolPolicy / bundle"] --> C["maqpna policy push<br/>--to agentcore"]
C -->|"Create / Update / DeletePolicy"| E["AgentCore<br/>policy engine"]
E --> G["AgentCore gateway<br/>enforces"]
CT["CloudTrail"] --> I["maqpna audit import"]
OA["OpenAI audit log"] --> I
AN["Anthropic Compliance API"] --> I
I --> L["imports ledger"]
L --> EV["maqpna evidence generate<br/>--imports"]
What is supported#
| Platform | Push policy | Pull audit | Model calls through the gateway |
|---|---|---|---|
| AWS Bedrock AgentCore | Yes, AgentCore Policy (Cedar) | Yes, CloudTrail | Yes, Bedrock route |
| OpenAI | No | Yes, organization audit log | Yes |
| Anthropic | No | Yes, Compliance API activity feed | Yes |
| Azure (OpenAI, Foundry) | No | No | Yes, Azure OpenAI route |
| Google (Vertex AI, Gemini) | No | No | Yes, Vertex AI route |
MAQPNA has no push or pull integration for the Azure or Google agent platforms. Route their model calls through the gateway instead: see Connect model providers.
Prerequisites#
- The
maqpnaCLI. - Your policies as
ToolPolicyfiles or apolicies.jsonbundle. See Write and test policies. - For the push: an AgentCore gateway and policy engine, and a gateway manifest (step 1).
- AWS credentials in the environment, in this order:
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY(development), IRSA (AWS_ROLE_ARNandAWS_WEB_IDENTITY_TOKEN_FILE, withAWS_REGION), or EKS Pod Identity or ECS container credentials (AWS_CONTAINER_CREDENTIALS_FULL_URIorAWS_CONTAINER_CREDENTIALS_RELATIVE_URI). Shared profiles (AWS_PROFILE,~/.aws/config) are not read. - For OpenAI imports: an admin key. For Anthropic imports: a Compliance Access Key, or an Admin API key
with
read:compliance_activities.
Push policy to AgentCore#
Step 1: Write the gateway manifest#
AgentCore knows tools by gateway target, not by MAQPNA server. The manifest maps each MAQPNA server to
an AgentCore target, with each tool's input schema. This is the start of
examples/integrations/agentcore/gateway-manifest.json:
{
"gatewayArn": "arn:aws:bedrock-agentcore:eu-west-1:123456789012:gateway/maqpna-coder",
"approvalAsDeny": true,
"principal": {"namespaceTag": "maqpna_ns", "agentTag": "maqpna_agent", "groupsTag": "maqpna_groups"},
"targets": [
{"server": "github", "target": "GitHub", "tools": [
{"name": "get_file_contents", "inputSchema": {"type": "object",
"properties": {"owner": {"type": "string"}, "repo": {"type": "string"}, "path": {"type": "string"}},
"required": ["owner", "repo", "path"]}}
]}
]
}
AgentCore has no approvals. approvalAsDeny: true compiles require_approval rules as denials; without
it they are refused. The full manifest format is in Policy export.
Step 2: Compile and read the plan#
The push compiles your policies exactly as maqpna policy compile --to agentcore does: one Cedar
statement per AgentCore policy, or an error naming the rule AgentCore cannot express (session taints,
the substrate, when, schedule and others). Without --apply, the push is a dry run:
maqpna policy push --to agentcore --manifest gateway-manifest.json \
--engine <policy-engine-id> policies/
The push lists every policy in the engine, then plans create, update, delete, unchanged or
conflict for each, with the SHA-256 of each statement. It also reads the AgentCore gateway named in
the manifest and warns if its policy engine configuration does not name your engine. Policies in an
unassociated engine are stored but not enforced.
| Flag | Default | What it does |
|---|---|---|
--to |
— | Target. Only agentcore. |
--manifest |
— | The gateway manifest. |
--engine |
— | The AgentCore policy engine ID. |
--owner |
default |
Owner label. Several installations can share one engine with different owners. |
--apply |
off | Execute the plan. |
--wait |
off | With --apply: wait for each phase to be ACTIVE or deleted. |
--wait-timeout |
5m |
With --wait: time allowed per phase. |
--ledger FILE |
— | With --apply: append one audit record per change. |
--enforcement-mode |
AgentCore's (ACTIVE) |
ACTIVE or LOG_ONLY for the pushed policies. |
--region |
the gateway ARN's, then AWS_REGION |
AWS region. |
--endpoint |
https://bedrock-agentcore-control.<region>.amazonaws.com |
Control-plane URL. |
--namespace |
default |
Namespace of ToolPolicies without metadata.namespace. |
The full flag list is on maqpna policy push.
Step 3: Apply#
maqpna policy push --to agentcore --manifest gateway-manifest.json \
--engine <policy-engine-id> policies/ --apply --wait --ledger push-ledger.jsonl
The push runs four phases, tightening first, so the engine is never looser part-way through than both the old and the new policy set:
- New and changed
forbidstatements. - Removed
permitstatements. - New and changed
permitstatements. - Removed
forbidstatements.
Every change in a phase is attempted. If any fails, the later phases are skipped and the command exits non-zero. Throttling (429) and server errors (5xx) are retried with backoff.
The AWS identity needs bedrock-agentcore:ListPolicies, GetPolicy, CreatePolicy, UpdatePolicy,
DeletePolicy and GetGateway. The push never changes the gateway itself. Associate the engine with the
gateway yourself, in the AWS console or with the AWS CLI.
Step 4: Verify#
Run the same push again. Every policy is unchanged:
maqpna policy push --to agentcore --manifest gateway-manifest.json --engine <policy-engine-id> policies/ -o json
With -o json you get the plan (each change with statementSha256 and previousSha256), the
outcomes, the gateway association and the warnings. With --ledger, each change is an audit record:
server policy-push/agentcore, tool create, update or delete, decision observe, and the ext
fields agentcoreEngine, agentcorePolicy, agentcorePolicyId and pushResult.
How ownership works#
The push only changes policies it owns. A policy belongs to an owner when its name starts with
maqpna_ and its description starts with the owner's marker:
[maqpna owner=default sha256=<statement sha256>] coder-policy/github-read: Reads are fine.
Every other policy in the engine is foreign: it is counted and never updated or deleted. A wanted name
already taken by a foreign policy is a conflict. So is a MAQPNA policy in CREATE_FAILED,
UPDATE_FAILED or DELETING.
A name is maqpna_, the <policy>/<rule> ID cut to 32 characters, then 8 hex digits of a SHA-256 over
owner and ID: at most 48 characters. Reordering rules does not rename the others.
Pull provider audit events#
Step 1: Import#
Use a dedicated imports ledger. Only one process may append to a ledger file, so never point it at a ledger a running gateway writes.
# LookupEvents API, region from AWS_REGION or --region
maqpna audit import --source cloudtrail --ledger imports.jsonl
# CloudTrail log files synced from S3 (.json, .json.gz), including data events
maqpna audit import --source cloudtrail --ledger imports.jsonl ./AWSLogs/
export OPENAI_ADMIN_KEY=<your-openai-admin-key>
maqpna audit import --source openai --ledger imports.jsonl
export ANTHROPIC_COMPLIANCE_ACCESS_KEY=<your-compliance-key>
maqpna audit import --source anthropic --ledger imports.jsonl
| Source | API | What it shows |
|---|---|---|
cloudtrail |
LookupEvents, one request per event source (default bedrock.amazonaws.com and bedrock-agentcore.amazonaws.com) |
Management events of the last 90 days in one region |
cloudtrail with files |
CloudTrail log files ({"Records": [...]}) |
Everything the trail logs, including data events. --event-source '*' imports every source. |
openai |
GET /v1/organization/audit_logs |
User actions and configuration changes (API keys, projects, members). Not model calls. |
anthropic |
Compliance API GET /v1/compliance/activities |
Authentication, chat, file, project, administrative and platform activity |
The command prints how many events it imported, how many were already recorded and how many were
malformed, then any unmediated events in a table with the columns SEQ, EVENT TIME, ACTION,
TOOLS, ACTOR and REFUSED BY GATEWAY. The last column counts how often the gateway refused the same
tool: the same thing, done outside the gateway.
Useful flags: --since and --until (RFC 3339, or a duration ago such as 72h), --dry-run,
--namespace and --agent to attribute events to a MAQPNA system, --raw-dir DIR to keep each raw
event as DIR/<source>/<sha256>.json, --type to filter OpenAI event or Anthropic activity types, and
--key-env to read the key from another variable. See maqpna audit import.
Step 2: Run it on a schedule#
Imports are incremental and safe to repeat:
- Dedupe by event ID. Events whose provider ID the ledger already holds are skipped.
- Cursor.
<ledger>.import-<source>.json(--state) records the end of the last complete window. The next window starts there minus--overlap(default 15 minutes). The first window is--lookback(default 24 hours). Log-file imports have no window and no cursor. - All or nothing per window. Every page is fetched before anything is appended. An error, or more
than
--max-events(default 50,000) events, appends nothing and leaves the cursor in place.
The gateway has no background importer. Run the command from cron or a Kubernetes CronJob.
Step 3: Use the imports as evidence#
maqpna evidence generate --framework eu-ai-act --system namespace:team-a --period 2026-Q4 \
--ledger audit.jsonl --imports imports.jsonl --key <checkpoint-key-uri>
Imported records are never MAQPNA decisions. Each has decision observe, server import/<source>,
reason imported:<source> (plus unmediated:<tools> when a hosted tool ran), and ext fields such as
importedEventId, importedActor and importedRawSha256. Evidence packs list them only under
External observations and in workpapers/external-observations.csv. They are left out of decision
counts, approvals, tool inventory, AI-BOM, cost and FOCUS exports. See
Generate a compliance evidence pack.
What counts as unmediated#
The import flags an event when its raw content shows what the gateway itself would refuse:
- a
toolsentry whosetypeis a provider-hosted tool, such asweb_search_20250305orcode_interpreter; - Gemini and Bedrock tool keys such as
googleSearch,codeExecutionandsystemTool; - provider-hosted MCP (
mcp_servers, or tools of typemcpormcp_toolset); - for CloudTrail, AgentCore's built-in tools:
StartCodeInterpreterSessionandInvokeCodeInterpreterascode_interpreter,StartBrowserSessionandConnectBrowserAutomationStreamasbrowser.
Limits#
- AgentCore only for push. Only AgentCore Policy (Cedar). The push checks the gateway association but does not set it.
- What providers log. Most providers log no request bodies, so a hosted tool used inside a model call shows up only where the log carries the request (for example CloudTrail data events).
- CloudTrail is one region per run. Reading log files straight from S3 is not built: sync them first
with
aws s3 sync. - Late events. API imports miss events delivered later than
--overlap. - Import time, not event time. An imported record falls in the evidence period it was imported in. The workpaper gives the provider's event time.
Troubleshooting#
| Symptom | Cause and fix |
|---|---|
cloudcreds: no credentials found for AWS |
No AWS credentials found. Set IRSA, Pod Identity or the access-key variables. |
IRSA needs AWS_REGION |
Set AWS_REGION for the regional STS endpoint. |
Warning: gateway ... is not associated with policy engine |
The policies are stored but not enforced. Associate the engine with the AgentCore gateway. |
Plan shows conflict |
A foreign policy holds the name, or a MAQPNA policy is in a failed or deleting state. Fix it in AgentCore, then push again. |
ConflictException on apply |
Another request is changing the policy. Push again. |
| Compile error naming a rule | AgentCore cannot express that rule. Change it, or set approvalAsDeny for approval rules. |
audit import: set OPENAI_ADMIN_KEY ... |
The key variable is empty. Export it, or name another one with --key-env. |
| Window refused for too many events | Narrow it with --until, or raise --max-events. |