unify/provider_proxy/
is a small FastAPI app bound to loopback that sits between sandboxed actor
code and the real Google / Microsoft APIs. It exists to solve two problems
at once: give LLM-authored code the complete provider REST surface
without a real token, and enforce the per-assistant file-access
allowlist at a single choke point.

Module map
| Module | Key symbols | Role |
|---|---|---|
proxy.py | build_app, ensure_proxy_running, _dispatch, _forward, _handle_write, _handle_batch | The FastAPI app and request pipeline; one catch-all route /{provider}/{rest_path} |
session.py | ProxySession, sandbox_env, build_sandbox_env, provider_token_env_keys | The nonce, the sandbox env overlay, and the scrub list |
classify.py | classify, Classification, Locator | Path-template matching → request kind |
policy.py | WorkspaceFilePolicy, PolicyStore, evaluate_access, decision_key, index_decisions | The allowlist model, mirrored from Orchestra |
ancestry.py | is_allowed, ancestry_chain, child_allowed, google_get, ms_get, ms_get_by_path | Resolving an item’s parent chain against the policy |
filter.py | filter_listing, filter_changes, _rewrite_pagination_links | Masking disallowed items out of listings, search, and delta responses |
ENDPOINTS.md | — | The supported path templates, the gating matrix, and how to add patterns |
microsoft → graph.microsoft.com, google →
www.googleapis.com.
The request pipeline
- Authenticate —
_dispatchrequiresAuthorization: Bearer {nonce}, where the nonce belongs to the currentProxySession. The sandbox got that nonce fromget_oauth_access_token; nothing else is accepted. - Classify —
classify(provider, method, rest_path, query)matches the request against known path templates and returns one of:KIND_NON_FILE(mail, calendar, contacts, any non-drive surface),KIND_FILE_READ,KIND_FILE_WRITE,KIND_BATCH(Graph$batch, handled per-subrequest by_handle_batch), orKIND_UNKNOWN— a file-shaped path that matched nothing, which is denied by default with a 403. Default-deny on unknowns is what makes the endpoint catalogue inENDPOINTS.mda real security boundary rather than documentation. - Enforce — file reads and writes are gated (next section);
non_filerequests pass straight through. - Forward —
_forwardinjects the real token viaget_provider_access_token_optimistic, and on a 401 forces a secret re-sync (refresh_provider_access_token) and retries once. Pagination links in responses are rewritten to point back through the proxy so follow-the-link code stays inside the policy boundary.
The policy model
AWorkspaceFilePolicy (per provider) is default_allow plus a tuple of
decisions, each {drive_id, item_id, allow} — exactly the shape the
Console’s file picker saves. Evaluation, in evaluate_access +
ancestry.is_allowed, walks the item’s parent chain (fetched live from
the provider via google_get / ms_get, item first, then ancestors): the
nearest explicit decision wins, and if no ancestor carries a decision
the policy’s default_allow applies. That’s the code behind the Console’s
“Selecting a folder grants access to everything inside it” and the
“New files accessible by default” toggle.
Two subtleties worth knowing:
- Gating is opt-in.
PolicyStore.get(provider)returningNone(no Console policy saved) means unrestricted passthrough. The moment a policy exists,default_allow=Falsesemantics kick in for anything unchecked. - Masked means invisible, not forbidden. Direct reads of a disallowed
item return 404 (not 403) so the item’s existence isn’t leaked, and
filter_listing/filter_changessilently drop masked items from listings, search results, and delta feeds. Writes into disallowed locations fail via_handle_writechecking parents and destinations.
SecretManager._sync_workspace_file_policy, which
mirrors Orchestra’s per-assistant workspace-file-access config into the
process-local PolicyStore on the same debounced cadence as secrets.
Actor integration
The proxy starts lazily:SessionExecutor.execute in
unify/actor/execution/session.py
calls ensure_proxy_running before any code runs. Sandbox code then uses
the drop-in base URLs from the environment:
MICROSOFT_GRAPH_BASE, GOOGLE_DRIVE_BASE, and GOOGLE_API_BASE are
documented to the model in the execute_code docstring
(unify/actor/code_act_actor.py)
and the _EXTERNAL_APP_INTEGRATION block in
unify/actor/prompt_builders.py.
Calendar, Gmail-API, and People requests ride the same proxy but classify
as non_file, so they pass through ungated — the file allowlist governs
files only.
Long-lived execution backends (venvs, shell sessions) get a fresh overlay
per execution via FunctionManager._get_runtime_oauth_env_overlay →
get_refresh_token_oauth_env_overlay, so a proxy restart or nonce
rotation never strands them.
Extending the endpoint catalogue
To support a new provider endpoint family, add its path template toclassify.py (and the gating matrix in ENDPOINTS.md). Anything
file-shaped you don’t add stays default-denied — the safe failure mode.
The classification and filtering behavior is pinned by
tests/provider_proxy/test_classify.py, test_proxy_enforcement.py,
test_filter.py, test_forward_auth.py, and test_session_env.py.