Guides
Security administration
The controls owners and admins set for a whole workspace (single sign-on, two-factor, session limits, IP allowlists, sign-in methods, domain capture, retention) and the records an administrator or auditor reads (sessions, the audit log, the roles matrix, trust data).
Everything on this page is enforced by the server on every request, not by the console. Owners, admins and security admins (the security capability) change the security policy, single sign-on, members’ sessions and retention, and choose where the audit log is streamed; owners keep billing, ownership and requests to join. See The security admin role. Each change is an audit entry, and audit entries are chained into the workspace’s evidence ledger. For identifiers, access profiles and entitlements, see Identity and access.
#The security policy
GET /api/w/:wid/security returns the policy, and PUT /api/w/:wid/security changes any part of it. Send only the fields you are changing.
{
"requireSso": true,
"requireMfa": true,
"sessionIdleMinutes": 30,
"sessionMaxHours": 12,
"ipAllowlist": ["203.0.113.0/24", "2001:db8::/32"],
"allowedMethods": ["sso", "passkey"],
"domainCapture": true
}The answer is the whole policy again, with changes (each field’s from and to) and sessionsEnded (people signed out by turning on two-factor). A change that would lock you out is refused with 409 would_lock_you_out, and nothing is saved.
#Require single sign-on
requireSso makes members whose email is on one of the workspace’s verified domains sign in through its OIDC single sign-on (see Workspace single sign-on). It needs single sign-on configured and at least one verified domain. Password, email-code and personal-provider sign-ins for those addresses are refused at sign-in, and a session made any other way is refused on its next request with 403 sso_required and an ssoUrl. People on other domains (contractors, say) are not affected.
Break-glass. An owner whose session was made with a passkey or an authenticator code is never refused by this rule, so a broken identity provider cannot lock the workspace out. Turning the rule on is refused if you are on a verified domain and did not sign in with single sign-on, unless you are an owner with a passkey or an authenticator app.
#Require two-factor
requireMfa requires every member to have an authenticator app or a passkey. Members without one are signed out at once and asked to enrol at their next sign-in; a session that somehow lacks one is refused with 403 mfa_required. People who reach the workspace through its single sign-on are left to the identity provider. You must have a second factor yourself before you can require it.
#Session limits
sessionIdleMinutes (5 or more) and sessionMaxHours (1 or more) set shorter limits than the deployment’s (IMMISCIBLE_SESSION_IDLE_MINUTES, IMMISCIBLE_SESSION_MAX_DAYS); they can never be looser. A person’s session follows the strictest limit of every workspace they belong to, and is checked on each request: past the idle or absolute limit it ends and the next request answers 401. null returns to the deployment’s limits.
#IP allowlist
ipAllowlist is up to 50 IPv4 or IPv6 addresses or CIDR ranges. When it is not empty, the console, the phone app, and every credential bound to the workspace (gateway and agent keys, service tokens, OAuth tokens for MCP clients) are refused from any other address with 403 ip_not_allowed. SCIM and inbound webhooks are not covered, because they come from your identity provider and vendors.
Saving a list that does not contain the address you are saving from is refused, and the error names that address. Behind a proxy, set TRUST_PROXY so the address is the client’s, not the proxy’s. An operator can suspend every allowlist with IMMISCIBLE_IP_ALLOWLISTS=off (see the deployment reference).
#Allowed sign-in methods
allowedMethods lists which of password, email (a link or code), google, microsoft, okta, passkey and sso reach the workspace; null allows all. A session is read by its first factor, so a password confirmed with an authenticator code is password. Other sessions are refused with 403 sign_in_method_not_allowed. The method you are signed in with must stay in the list, and sso needs single sign-on set up. The owner break-glass above applies here too.
#Domain capture
With domainCapture on (the default), someone signing up with an address on a verified domain does not get a team workspace of their own: their account is made and they ask to join yours. A personal workspace is still theirs to make. See Requests to join.
To try domain capture on a laptop without a DNS record, start the server with IMMISCIBLE_DEV_VERIFY_DOMAINS=true: verifying a domain then succeeds without the TXT record. The server refuses to start with it in production.
#Sessions and devices
Each person sees their own sessions at GET /api/me/sessions and ends one with DELETE /api/me/sessions/:sid. Each session lists device, os and browser (read from the browser’s User-Agent), ip, location, signInMethod, createdAt and lastSeenAt. location is only “This computer” or “Private network”: there is no geolocation service, so for any other address the console shows the address itself.
Owners, admins and security admins list a member’s sessions and phones with GET /api/w/:wid/members/:uid/sessions and end them with DELETE /api/w/:wid/members/:uid/sessions. Where the workspace owns the person’s identity (their domain is verified here, the deployment is self-hosted, or this is their only workspace) every session ends and their phones are signed out (scope: "everywhere"). Otherwise their sessions stop reaching this workspace only (scope: "workspace") and their other workspaces are untouched. Only an owner ends an owner’s sessions.
#Alert emails
Each person chooses which alert emails they get at GET /api/me/email-preferences and PUT /api/me/email-preferences ({ "muted": { "spend_alerts": true } }). Three can be turned off: orphaned_agents, shadow_keys and spend_alerts. Each of those emails carries a one-click unsubscribe (RFC 8058: List-Unsubscribe with List-Unsubscribe-Post) signed for that address and that alert alone, so a mail client can turn it off without anyone signing in. Opening the link shows a page that asks first, and offers to turn it back on.
Approval requests, sign-in and step-up codes, and the two incidents that stop an agent (a payment charged more than was approved, records moved beyond what was declared) always arrive and cannot be turned off.
#The audit log
GET /api/w/:wid/audit lists what people did, newest first: sign-ins, role changes, rules, approvals, stops, settings, keys and integrations. Each entry has the actor, a plain title, the subject, the IP address, the browser and the time. Agents’ and machines’ actions are in the activity feed and the evidence ledger instead.
| Query | Meaning |
|---|---|
actor | A user id or email. |
kind | sign-in, roles, members, approvals, stops, rules, keys, settings, integrations, agents, evidence or other. |
since, until | A date (2026-10-01) or a time. |
cursor | The nextCursor of the previous page. |
limit | Up to 500 a page; 50 by default. |
format | csv or json downloads every matching entry. |
Owners, admins, auditors and security admins read it. It reads the same audit records the evidence ledger chains, so nothing is stored twice, and it shows as far back as the plan’s history window (retentionDays, and readable in words): 7 days on Free, a year on Team, ten years on Business and Enterprise. Entries older than the window are not deleted; they stay in the ledger. Every export is itself an entry.
#The audit stream
Every audit entry is chained into the evidence ledger as an audit record, and every ledger record can be pushed as it is written, so the audit log reaches your SIEM without anyone downloading it:
- Splunk (HTTP Event Collector) and Datadog Logs, as OCSF, batched, with up to six attempts and backoff.
- Signed webhooks to your own endpoint, HMAC-SHA256, retried up to five times.
GET /api/w/:wid/audit/stream says which destinations carry the audit log, whether each takes every record (complete) or only audit records, and when each last delivered (lastDeliveredAt), with failures and the last error. The console shows it under Settings, Audit stream.
One honest edge: when one request both changed something and wrote its own ledger record, that record stands for the audit entry rather than a second audit record. A destination that takes every record misses nothing; one that takes only audit records misses those, so choose every record for a complete stream.
#Data retention
GET /api/w/:wid/retention and PUT /api/w/:wid/retention set how long two kinds of data are kept, in whole days, up to the plan’s limit (limits.maxDays):
callLogDays: model calls (model, tokens, cost, latency, routing), tasks left with no calls, and MCP proxy calls.promptMetadataDays: the prompt digests kept with each call, and the digests of untrusted content seen in agent sessions.
A daily job removes what is older and writes a retention_purge record to the ledger saying what it removed. Nothing is purged until an owner or admin sets a value.
#Requests to join
GET /api/w/:wid/join-requests lists pending requests (?status=approved, denied or all for others). An owner decides with POST /api/w/:wid/join-requests:
{ "id": "jrq_9c1e...", "decision": "approve", "role": "member" }Owners are emailed when a request arrives (“Maya asked to join Amethyst”, with a Review button). The person is emailed the answer: “You’re in: Amethyst on Immiscible” with their role in plain words and a Sign in button, or a short note that the request was not approved.
The role is any but owner, and defaults to the single sign-on default role. A request from an address nobody has confirmed yet (emailVerified: false) cannot be approved. The person sees their own requests at GET /api/me/join-requests.
#The roles matrix
Seven roles: owner, admin, security admin (security), approver, member, analyst and auditor. GET /api/w/:wid/roles returns what each capability allows and every workspace route with the roles that reach it. The table lives in the code, and the test suite calls every workspace route as every role and fails if any answer differs from it.
#The security admin role
security is for the security team, so it no longer needs full admin. It changes the security policy, single sign-on and retention, lists and ends members’ sessions (never an owner’s), reads the audit log and evidence, and manages where the audit log is streamed (webhooks, Splunk, Datadog). It cannot touch billing, members’ roles or invitations, agents, rules, keys or workspace settings. Making someone a security admin needs the same recent proof of identity as making them an admin, and a SCIM roles value or an owner approving a request to join can assign it. It is not offered as single sign-on’s default role for first sign-ins.
#Trust data
/trust.json lists the controls this deployment enforces, generated from its configuration, for security questionnaires: session limits, two-factor, single sign-on, IP allowlists, retention, encryption of secrets at rest, and what is not supported. SAML 2.0 single sign-on is supported per workspace (SP-initiated, signed assertions required), alongside OIDC with Okta, Entra and Google Workspace.