Policy-enforced observability for tool-using, multi-agent systems.
AstraGraph sits in front of MCP and A2A traffic, evaluates every action against policy, and writes a causal graph plus audit trail you can query in real time.
- Prevent unsafe tool calls before execution with fail-closed enforcement.
- Reconstruct who did what and why as a causal coordination graph.
- Investigate violations fast with searchable audit records and workflow-level traces.
- Support multi-agent workflows where MCP and A2A interactions mix in one run.
%%{init: {'theme':'base','themeVariables':{
'primaryColor':'#e6fffa',
'primaryTextColor':'#102a43',
'primaryBorderColor':'#1f7a8c',
'lineColor':'#1f7a8c',
'secondaryColor':'#f0fff4',
'tertiaryColor':'#fffaf0'
}}}%%
flowchart LR
AGENTS["Agents (MCP + A2A)"] --> PROXY["AstraGraph Proxy (Rust)"]
PROXY --> POLICY["Policy Service (Rust)"]
PROXY --> GRAPH["Graph Service (Rust)"]
PROXY --> VERIFIER["Verifier Service (gRPC contract; startup behavior configurable)"]
GRAPH --> DASH["Dashboard (React/Vite)"]
POLICY --> DASH
POLICY --> POLICYFILES["Policy YAML Files"]
GRAPH --> DATASTORE["Graph + Audit Data"]
classDef edge fill:#e6fffa,stroke:#1f7a8c,stroke-width:2px,color:#102a43;
classDef core fill:#fffaf0,stroke:#f59e0b,stroke-width:2px,color:#102a43;
classDef store fill:#f0fff4,stroke:#2f855a,stroke-width:2px,color:#102a43;
class AGENTS,DASH edge;
class PROXY,POLICY,GRAPH,VERIFIER core;
class POLICYFILES,DATASTORE store;
sequenceDiagram
participant A as Agent
participant P as Proxy
participant S as Policy
participant V as Verifier
participant G as Graph
A->>P: MCP tool call / A2A task
P->>S: Evaluate(policy_id, action, context)
alt policy denies
S-->>P: deny + rule_id
P->>G: Write blocked action/audit metadata
P-->>A: 403 POLICY_VIOLATION
else policy allows
S-->>P: allow + threshold/fallback
P->>V: Score deviation (or timeout)
alt verifier unavailable and fallback=QUEUE
P->>G: Write queued action/audit metadata
P-->>A: 403 POLICY_VIOLATION (QUEUE detail)
else verifier response available
V-->>P: score, rationale
P->>G: Write action + verification metadata
alt missing trace and tool not allowlisted
P-->>A: 403 POLICY_VIOLATION
else score within threshold
P-->>A: Forward to upstream and return result
else score violates policy
P-->>A: 403 POLICY_VIOLATION
end
end
end
- Docker Desktop
- Python 3.11+ (3.12+ recommended)
make- Rust toolchain (only needed for local non-container runs)
From this directory:
./scripts/e2e_run.shTo run E2E with the real verifier service path instead of the deterministic mock:
./scripts/e2e_run.sh --real-verifierTo include verifier-outage queue fallback coverage in the E2E run:
./scripts/e2e_run.sh --queue-fallbackThe script:
- Generates local TLS certs (
make certs) - Starts core services + 3 fixture agents via Docker Compose
- Runs
scripts/e2e_three_agent_gate.py --scenario standard - Optionally runs
--scenario queue-fallbackwith verifier stopped (--queue-fallback) - Tears everything down
- The proxy requires a verifier gRPC dependency by default (
ASTRAGRAPH_VERIFIER_REQUIRED_AT_STARTUP=true). - You can allow degraded startup with
ASTRAGRAPH_VERIFIER_REQUIRED_AT_STARTUP=false(proxy starts and enforces fallback behavior when verifier is unavailable). - Default E2E mode uses
scripts/mock_verifier.pyfor deterministic CI and local testing. --real-verifierusesverifier/server.py(reference Python implementation).- Production deployments should replace the verifier backend with your own service that implements the same gRPC interface documented in
verifier/INTERFACE.md. - If verifier scoring is unavailable and policy fallback is
QUEUE, AstraGraph returns a queue-fallback policy violation envelope.
Expected final output:
{"workflow_id":"wf-three-agent-e2e","status":"pass", ...}The gate validates all core controls in one run:
- A2A task handoff succeeds (
/a2a/tasks/send) - Malformed MCP JSON is rejected by upstream (
400 invalid json) - Safe MCP tool call is allowed (
safe_tool) - Missing-trace MCP call is blocked (
review_summarywithoutthinking->403 POLICY_VIOLATION) - Risky tool call is blocked (
export_data->403 POLICY_VIOLATION) - Block reason includes policy rule (
rule-export-block) - Graph store contains both allowed + blocked action nodes
- Audit endpoint returns a persisted violation record
- Queue fallback path returns
POLICY_VIOLATIONenvelope withQUEUEdetail when verifier is unavailable (--queue-fallback)
docker compose up --buildUse profile env files to make runtime intent explicit:
docker compose --env-file ops/profiles/dev.env up --build
docker compose --env-file ops/profiles/non-dev.env up --buildops/profiles/dev.env: local development defaults (ASTRAGRAPH_E2E_VERIFIER_MODE=mock, non-blocking verifier startup).ops/profiles/non-dev.env: staging/production-like defaults (ASTRAGRAPH_E2E_VERIFIER_MODE=real, verifier required at startup, fail-closed).
make certs
make dev-test
make dev-dashboard
make proto-gen
cargo test -p astragraph-policy policy_regression_packs_passSingle scenario:
cargo run -p astragraph-policy --bin policy_simulator -- \
--policy policy-bundles/e2e-policy.yaml \
--agent lead-scorer \
--tool export_data \
--args '{"table":"customers"}' \
--now-utc "10:30 UTC"Regression pack:
cargo run -p astragraph-policy --bin policy_simulator -- \
--pack tests/policy_regressions/finance_guardrails.yaml --strictEnable advanced policy mode in simulation:
cargo run -p astragraph-policy --bin policy_simulator -- \
--policy policy-bundles/e2e-policy.yaml \
--agent lead-scorer \
--tool export_data \
--advanced-modeMigrate YAML rules to advanced mode suggestions:
cargo run -p astragraph-policy --bin policy_migrate -- \
--input policy-bundles/e2e-policy.yaml \
--engine OPA_COMPAT \
--output /tmp/e2e-policy-v2.yamlBy default:
- URL:
http://localhost:5173 - Graph API base:
http://localhost:8080(override withVITE_GRAPH_API) - Built-in incident triage workflows:
- incident timeline (timestamped violation stream)
- drift-path surfacing (node + drift chain drill-down)
- policy hit analytics (top rules/agents/workflows)
- SLO slices (p50/p95/p99 latency, block rate, false-positive review queue)
Graph service (:8080, requires Authorization: Bearer <token>):
GET /graphsGET /graphs/:idGET /graphs/:id/nodesGET /graphs/:id/drift-path/:node_idGET /audit/violationsGET /audit/violations/:idGET /audit/slo(latency/block-rate/review-queue SLO slices)GET /audit/export(format=csv|json, optionalschema=soc2_v1|iso42001_v1)
Policy service (:8081, requires bearer token):
GET /policiesGET /policies/:namePOST /policies/validate(optionalsignaturefield when bundle signing is enabled)GET /policies/:name/historyGET /policies/:name/rolloutPOST /policies/:name/rollout(start/update canary rollout, optionalsignature)POST /policies/:name/rollout/promote(promote candidate to stable)POST /policies/:name/rollback(rollback active rollout)
Compatibility reference: docs/api_policy_compatibility_matrix.md
Advanced policy mode reference: docs/advanced_policy_mode.md
Model upgrade protocol: docs/model_upgrade_protocol.md
Enterprise reference architecture: docs/enterprise_reference_architecture.md
Kubernetes multi-tenant deployment: docs/kubernetes_multi_tenant_reference.md
Adoption playbooks: docs/adoption_playbooks.md
Public roadmap: docs/public_roadmap.md
Community contribution track: docs/community_contribution_track.md
Proxy HTTP entrypoint (:7070):
POST /mcp/tools/callPOST /a2a/tasks/send
curl -H "Authorization: Bearer dev-token" \
http://localhost:8080/graphscurl -H "Authorization: Bearer dev-token" \
"http://localhost:8080/audit/violations?workflow_id=wf-three-agent-e2e"curl -X POST -H "Authorization: Bearer dev-token" -H "Content-Type: application/json" \
"http://localhost:8081/policies/e2e-policy/rollout" \
-d '{"percentage":20,"yaml":"apiVersion: astragraph.io/v1\nkind: AgentPolicy\nmetadata:\n name: e2e-policy\n version: \"1.1\"\n owner: \"astragraph-dev@local\"\nspec:\n agents:\n - name: lead-scorer\n tier: 3\n allowed_tools: [safe_tool, review_summary, export_data, a2a.tasks.send]\n blocked_tools: []\n rules:\n - id: rule-export-block\n description: Block export_data in e2e gate\n condition: \"action.tool == export_data\"\n action: BLOCK\n verification:\n threshold: 0.7\n model: \"mock-verifier\"\n fallback: ALLOW\n","signature":"'"$SIGNATURE"'"}'curl -X POST -H "Authorization: Bearer dev-token" \
"http://localhost:8081/policies/e2e-policy/rollback"- Policy service emits rollout telemetry:
astragraph.policy.rollout.events.total(labels:policy,event,status)astragraph.policy.rollout.active(active rollout up/down counter by policy)
- Optional webhook hook for rollout lifecycle events:
ASTRAGRAPH_POLICY_ALERT_WEBHOOK_URLASTRAGRAPH_POLICY_ALERT_WEBHOOK_TOKEN(optional bearer token)
- Optional signed policy bundle enforcement:
- Set
ASTRAGRAPH_POLICY_BUNDLE_SIGNING_KEY=<shared-secret>on the policy service. - When enabled,
POST /policies/validateandPOST /policies/:name/rolloutrequiresignature(JWT signed withHS256whose payload contains the exact raw YAML underyaml). - Signature example:
- Set
export ASTRAGRAPH_POLICY_BUNDLE_SIGNING_KEY='dev-shared-secret'
POLICY_YAML="$(cat policy-bundles/e2e-policy.yaml)"
SIGNATURE="$(
python3 -c "import base64, hashlib, hmac, json, os; y=open('policy-bundles/e2e-policy.yaml').read(); h={'alg':'HS256','typ':'JWT'}; c={'yaml':y}; e=lambda o: base64.urlsafe_b64encode(json.dumps(o,separators=(',',':')).encode()).rstrip(b'='); hp=e(h)+b'.'+e(c); s=base64.urlsafe_b64encode(hmac.new(os.environ['ASTRAGRAPH_POLICY_BUNDLE_SIGNING_KEY'].encode(), hp, hashlib.sha256).digest()).rstrip(b'='); print((hp+b'.'+s).decode())"
)"- Prometheus alert rule examples:
ops/prometheus/astragraph-policy-rollout-alerts.yaml
eval/agentbench_eval.py: FAR/VDR gate oneval/agentbench.jsonleval/synthetic_attack_eval.py: synthetic malicious/benign gate ontests/synthetic/attack_traces.jsonleval/anonymized_trace_eval.py: anonymized trace gate ontests/anonymized/anonymized_traces.jsonleval/model_upgrade_gate.py: candidate-vs-baseline verifier upgrade gateeval/review_feedback_loop.py: reviewer feedback aggregation into tuning actions- CI enforces all gates against the proxy fixture before merge.
proxy/: Rust sidecar proxy and enforcement layer (MCP + A2A interceptors)policy/: Rust policy engine crate (cargoworkspace member), serving/policies/*APIspolicy-bundles/: Sample/versioned policy YAML bundles used for quickstart and simulationgraph/: Rust graph and audit service (REST + gRPC)verifier/: Verifier reference implementation + scoring code (verifier/INTERFACE.mddefines the production contract)dashboard/: React + Vite operator UIconnectors/: LangGraph, CrewAI, AutoGen adapters + sharedProxyClient+quickstart.pyops/: ops artifacts (Prometheus alert rules + runtime profiles)scripts/: E2E gate, fixtures, mocks, cert generationtests/: integration, synthetic, and anonymized evaluation assetsdata/: Local runtime state (policy history and graph/audit JSONL files for dev/e2e)charts/: Helm chart manifestsdocs/schemas/: SOC2 + ISO42001 audit export schemasdocs/repo_structure.md: contributor-oriented map of naming conventions and top-level directories
- Default mode is intended to be fail-closed (
fail_closed = trueinastragraph-proxy.toml). - In production, keep
ASTRAGRAPH_VERIFIER_REQUIRED_AT_STARTUP=trueunless you intentionally run degraded startup with strict fallback (QUEUEorBLOCK). - Local certs in
certs/are for development. Use managed PKI in production. - Do not expose demo tokens or local auth settings in internet-facing deployments.
- Use
./scripts/e2e_run.sh --real-verifierto exercise the real verifier path in E2E. - Use
./scripts/e2e_run.sh --queue-fallbackto exercise verifier-outage queue fallback coverage. - Add organization auth provider and stricter role mapping for graph/policy APIs.
- Back graph/audit storage with durable external DB for high-volume workloads.
- Extend policy regression packs in
tests/policy_regressions/and keep them green in CI.
Apache-2.0. See LICENSE.
This repository now includes the AgentStack GitAgent overlay:
agent.yamlfor identity, policy binding, and skill manifestSOUL.mdfor operator-facing identity and missionRULES.mdfor human-readable constraints mapped to runtime policymemory/for auto-committed audit/test historyskills/for executable capability descriptorshooks/for pre/post execution governance automation
The overlay is additive: existing APIs and runtime behavior are unchanged unless these new files/hooks are explicitly used.