Assistants speak MCP to Aggrete instead of to your systems, which are reachable only through it, so there is no path around the policy. And because assistants reach it by speaking standard MCP, there is no endpoint agent, browser extension or client change on anyone's device; control lives on the data path, not the laptop. Every call is evaluated against the policy and the caller's recent history. There is no model in the enforcement path, so a decision is reproducible and auditable.
Connectors are network-fenced so only the proxy can reach them; the proxy holds the credentials, never the user. An optional read-only console reads the two files the proxy writes.
Full diagram, the Google Drive connector, and the OAuth sign-in flow are documented in the README.
New rule types are added in Engine.pre_call / Engine.post_call and dispatched by the type: key. Prefer pre-call decidability where possible. Every rule takes optional scoping: allowed_users, blocked_users, since, until.
| type | phase | what it decides |
|---|---|---|
| domain_join | pre | Refuses the call that would complete a forbidden set of domains for one person (e.g. personnel + budget + rotation), with entity overlap required by default. |
| domain_block | pre | A domain that assistants may never reach (legal hold, privileged material). |
| wall | pre | Who may reach a domain, and until when: embargoes, investigation walls, privilege, restricted health/absence data. |
| min_group | post | Aggregate-only answers: a result naming fewer than k people is treated as one person's data. |
| self_comparison | post | The requester's own record placed next to colleagues' in the same domain, the precondition for benchmarking teammates. |
| entity_budget | post | Caps the number of distinct people one user may accumulate in a domain over the window. |
| flow | pre | Prompt-injection shield: once a session has read untrusted content, it may not reach an egress domain. Any write counts as egress. |
| arg_match | pre | Decides a call from its arguments, not just its kind: allow an export scoped to your own team, refuse the same export scoped to the whole company. Operators: equals, in, regex, gt, lt, exists, missing. |
Writes (create, update, upload, post, send) are governed as egress; a rule can target writes only with applies: write. Rules group into protection packs you toggle on and off. Actions start at alert and move to deny after tuning; the test harness requires an allow and a deny test per rule.
The rules decide what a request may assemble. These run around every call, each deterministic and opt-in from the config, and each writes to the same audit line.
| control | what it does |
|---|---|
| check | Ask whether a sequence of calls would be allowed before running any of them: the decision, the rule, the clause and the fix, with nothing fetched. A built-in aggrete__check tool. |
| tool_integrity | Fingerprint every upstream tool on first sight; flag a later change to its description or schema (a rug pull), and scan descriptions for hidden instructions (poisoning). Alert or block. |
| rate_limit | A per-user ceiling on calls per window, shared across replicas via Redis. A denial-of-wallet and abuse control. |
| scan_inbound | Scan tool arguments for credential-shaped strings and block or mask them before they reach an upstream. |
| audit_forward | Stream each audit row to Splunk, Elastic, Datadog or syslog as it is written, off the hot path. The hash-chained local log stays the system of record. |
| aggrete-lint | Static checks on the policy for fail-open and dead rules: a critical rule that only alerts, an expired embargo, an unreachable domain. Exits non-zero for CI. |