Admin guide · 0.1.0

Run it for a team.

One policy file, installed the way you install any managed setting, controls what Asylum may do on every Mac you manage. Activity data stays on each Mac, and you can pull it with an API or stream it to your collector.

The policy file

Asylum reads /Library/Application Support/asylum/policy.json at launch and again every minute. Only administrators can write there, so users can see the policy but can't change it. When a policy is installed, Settings shows Managed by <organization>; if the file can't be parsed, Settings says so and the last good policy stays in force.

{
  // Comments are allowed.
  "organization": "Acme",
  "network": { "mode": "allowlist", "allowed": ["acme.com", "github.com", "*.atlassian.net"] },
  "local-egress": false,
  "local-exec": "ask",
  "auto-review": true,
  "rules": [
    { "kind": "ask", "text": "Before emailing anyone outside acme.com." },
    { "kind": "allow", "text": "Reading public documentation sites." }
  ],
  "disabled-plugins": ["stripe"],
  "required-plugins": ["github", "linear"],
  "templates": "team",
  "team-agents": ["asylum://template/…"],
  "cloud-agents": false,
  "setup-script": "brew bundle --file=/opt/acme/Brewfile",
  "check-script": "test -f ~/.acme/enrolled",
  "terminate-inactive-days": 30,
  "otel-endpoint": "https://otel.acme.internal:4318"
}

Every key is optional; an empty object — or no file — means unmanaged. Set ASYLUM_POLICY to try a policy from another path before rolling it out.

Deploying it

Any tool that can put a root-owned file on a Mac works: an MDM's custom file payload, a package, or a script.

sudo mkdir -p "/Library/Application Support/asylum"
sudo cp policy.json "/Library/Application Support/asylum/policy.json"
sudo chown root:wheel "/Library/Application Support/asylum/policy.json"
sudo chmod 644 "/Library/Application Support/asylum/policy.json"

Changes apply within a minute, without restarting Asylum. Each change is written to the audit log as policy.applied with the admin as its actor.

Reference

KeyTypeEffect
organizationstringShown as "Managed by …"
networkobjectNetwork Controls: mode, allowed, blocked
local-egressboolfalse cuts network access for commands run on the user's own Mac
local-execnever · ask · alwaysCeiling for Execution on Local Computer; stricter wins
auto-reviewboolForce Auto-review on or off
rulesarrayLocked Auto-review rules: {"kind": "ask"|"allow", "text": "…"}
disabled-pluginsarrayMarketplace ids or names Agents may not use
required-pluginsarrayMarketplace ids added for everyone, which users can't remove
templatespublic · team · offThe widest template sharing allowed
team-agentsarrayTeam Agent links added for everyone; they can't be hidden or deleted
team-agents-enabledboolfalse turns Team Agents off
cloud-agentsboolfalse turns off the Claude Code and Codex CLI providers
setup-script / check-scriptstringShell run on the computer; see setup scripts
terminate-inactive-daysnumberDelete a computer's data after this many idle days (0: never)
otel-endpointstringOTLP/HTTP collector for every user; overrides theirs

Marketplace ids: github linear notion jira jiratoken sentry stripe supabase vercel huggingface deepwiki context7 google slack pagerduty brave postgres playwright datadog salesforce figma.

Network Controls

modeAgents can reach
open (or unset)Anywhere
defaultAnywhere except the hosts in blocked
allowlistOnly the hosts in allowed
offlineNothing outside the Mac

An entry matches the host and its subdomains: acme.com covers docs.acme.com but not acme.com.evil.io; a leading *. is accepted and means the same.

Enforcement doesn't rely on the model behaving. Asylum runs a small filtering proxy on 127.0.0.1:

  • the Agents' browser launches through it, so every page, redirect, and script request is checked;
  • commands on the computer get it as HTTP(S)_PROXY, and the sandbox denies them any other outbound connection — a tool that ignores the proxy simply can't connect;
  • the web search, page fetch, and link preview tools go through it too.

Blocked requests get 403 Blocked by your admin's network policy. Changing the mode relaunches the browser on its next use. Model providers and connectors are Asylum's own connections, not Agent browsing, and aren't filtered — control those with disabled-plugins and your network.

Connectors

A disabled connector shows Disabled by your admin in the Marketplace, can't be added, and is never offered to Agents even if it was added earlier. A required connector is added automatically for everyone and can't be removed; each user still signs in with their own account.

Rules and review

Policy rules appear at the top of Auto-review Rules with a lock ("Required by your admin") and can't be edited or deleted. Users can add their own rules beside them; "Ask first" still wins a conflict. With auto-review set, the switch is locked to your value.

Execution

local-exec sets a ceiling on running commands and reading files on the user's own Mac: a user can be stricter, never looser. Below always, the approval card's Always allow is disabled. local-egress: false runs those local commands in a sandbox with outbound network denied.

Team Agents

List Team Agent links in team-agents and every user gets them, marked Required by your admin — they can't be hidden or deleted. Remove a link and the Agent stays but becomes optional. team-agents-enabled: false turns Team Agents off; users opening a Team Agent link see Team Agents Not Available. templates limits sharing: team allows only team-only links, off none.

Setup scripts

setup-script runs once on each Mac's computer, in its workspace and sandbox, and again only when you change the script. check-script runs every time the computer starts. A non-zero exit sends the user a notification with the output and writes computer.script_failed to the audit log; Agents keep working either way. Scripts are subject to Network Controls like anything else on the computer.

Computers

terminate-inactive-days erases a computer that hasn't been used for that long — workspace files, browser sign-ins, and backups — and logs computer.terminated_inactive. Agents and conversations stay; the computer starts fresh next time. Users can also Recreate, Stop, or Delete computer and data themselves in Settings → Computer.

Activity

Each Mac keeps its own records in its database; nothing is sent anywhere unless you set an OpenTelemetry endpoint or call the Admin API. Users see the same data in Settings → Activity: a week of Insights, the two logs, and export.

Action Recording

One entry per tool call: time, Agent, conversation, tool, target (a URL, path, or recipient — at most 500 characters), outcome, and duration. Outcomes are done, error, denied (by a rule or Auto-review), user-denied, and expired. Arguments and results are never recorded. Entries are kept for 90 days.

Audit log

Control-plane changes, kept indefinitely: settings.changed (which keys, not values), rule.added, rule.deleted, plugin.added, plugin.removed, policy.applied, computer.updating, computer.recovering, computer.recreating, computer.resetting, computer.update_scheduled, computer.setup, computer.script_failed, computer.terminated_inactive, conversation.exported, and conversations.exported. Repeats of the same change within 30 seconds are one entry. The actor is user or admin.

Export

Conversation content export writes each conversation as JSON and Markdown: every message's author, time, and text, plus each tool call's input, output, and status. Users export one conversation from its sidebar menu or all of them from Settings → Activity; the API exports one by id.

{
  "id": "…", "title": "Bug Reproducer", "kind": "direct",
  "messages": [
    { "at": 1759312440000, "role": "user", "author": "user", "text": "Reproduce #482", "tools": [] },
    { "at": 1759312468000, "role": "agent", "author": "Bug Reproducer", "text": "Bisected to 9f3c2e1.",
      "tools": [ { "tool": "shell", "input": { "command": "git bisect run ./repro.sh" }, "output": "…", "status": "done" } ] }
  ]
}

OpenTelemetry

Set otel-endpoint in the policy (or a user sets Collector endpoint in Activity) and Asylum sends a span for every Agent turn and tool call to <endpoint>/v1/traces as OTLP/HTTP JSON. Spans carry ids and names only — never message text, arguments, or results:

SpanAttributes
turnasylum.agent asylum.chat asylum.origin (chat, routine, handoff…) asylum.outcome
toolasylum.agent asylum.chat asylum.tool asylum.outcome

Every span has service.name = asylum and asylum.surface = asylum.agent. Delivery is best-effort; a collector that's down doesn't slow Agents down.

Admin API

Turn on Local Admin API in Settings → Activity. It listens on http://127.0.0.1:7438/v1 and needs Authorization: Bearer <token>; Copy token copies it from the Keychain, where it's created the first time.

RequestReturns
GET /v1/agentsAgents: id, name, status, kind, model
GET /v1/actions?since=<ms>Action Recording, newest first
GET /v1/audit?since=<ms>Audit log, newest first
GET /v1/insights?since=<ms>Counts, outcomes, top tools and Agents
GET /v1/chats/{id}/exportOne conversation's content
POST /v1/chats/{id}/messagesSend {"text": "…"} as the user; returns the message id
TOKEN=$(pbpaste)
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7438/v1/insights | jq
curl -s -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"text": "Run the weekly report now."}' \
  http://127.0.0.1:7438/v1/chats/<chat-id>/messages

A missing or wrong token gets 401; a bad request 400 with {"error": "…"}. Turning the API off takes effect after a restart.

What isn't covered

Asylum runs entirely on each person's Mac; there is no shared server. That means no SCIM, central group management, invitations, per-group policy overrides, or billing — distribute the policy file and Team Agent links instead.

Questions or a control you need? Open an issue.