Sim AI operator guide¶
How the Sim AI deployment is put together, how Illinois Chat talks to it, how users get in, and what to know before changing or upgrading it.
If you build tools in Sim rather than maintain the deployment, read the Sim user guide instead.
1. What runs¶
Sim is deployed from upstream container images only — there is no Sim source checkout
and no fork. The stack is defined in infra/docker/docker-compose.sim.yaml, layered on top
of infra/docker/docker-compose.dev.yaml:
| Service | Purpose | Local port |
|---|---|---|
simstudio |
The Sim app | 3010 |
sim-realtime |
Collaborative editing socket server | 3011 |
sim-db |
Sim's own Postgres (pgvector image), isolated from the app database | 55432 |
sim-migrations |
Runs Sim's schema migrations, then exits | — |
sim-approval-setup |
Installs the user approval gate, then exits | — |
sim-keycloak-setup |
Creates/updates the Sim OIDC client in Keycloak, then exits | — |
sim-sso-setup |
Registers the SSO provider inside Sim, then exits | — |
Sim keeps its own database, separate from Illinois Chat's. Nothing in Sim writes to the app's Postgres.
Images are pinned by digest, deliberately¶
SIM_APP_IMAGE, SIM_REALTIME_IMAGE and SIM_MIGRATIONS_IMAGE pin exact digests rather
than tracking a tag. This is not incidental: the approval gate patches Sim's own user
table, so an unreviewed upgrade could break it — or silently open it. See section 6.
The pinned app image is Sim v0.8.4, built from upstream commit e741923f on
2026-08-18. Knowing this matters more than it sounds; see the caveat in section 8.
2. Running it locally¶
bash infra/scripts/start-dev.sh # app stack + Sim
bash infra/scripts/start-dev.sh --no-sim # app stack only
bash infra/scripts/stop-dev.sh # stop everything
bash infra/scripts/stop-dev.sh --no-sim # stop the app stack, leave Sim alone
The scripts always invoke Compose as --project-directory . -f <dev file> -f <sim file>
from the repository root. Reproduce that exactly if you run Compose by hand; the Sim file
has no top-level networks: key of its own and will not parse standalone.
Secrets¶
Six values have no defaults and the stack refuses to start without them:
SIM_POSTGRES_PASSWORD, SIM_BETTER_AUTH_SECRET, SIM_ENCRYPTION_KEY,
SIM_API_ENCRYPTION_KEY (exactly 64 hex characters), SIM_INTERNAL_API_SECRET and
SIM_KEYCLOAK_CLIENT_SECRET. The start scripts generate deployment-specific values into
the root .env on first run.
Never let these regenerate where real data lives. SIM_ENCRYPTION_KEY and
SIM_API_ENCRYPTION_KEY are what make stored Sim data and API keys readable; regenerating
them invalidates sessions and makes previously encrypted data undecryptable.
SIM_APPROVAL_ADMIN_EMAIL is also required — it names the bootstrap platform admin.
One sharp edge¶
SIM_SSO_DOMAIN takes a single registrable domain, not a list. A comma-separated value
normalises to nothing and denies every sign-in, and start-dev.sh refuses to start on one.
Set it to the one domain your users sign in with (the hosted site uses illinois.edu) and
nothing else.
3. How Illinois Chat talks to Sim¶
Credentials are per project, stored server-side, and never accepted from the caller. A
project admin pastes a Sim API key (sk-sim-…) and workspace ID on the project's Tools
page; the key is encrypted at rest with ENCRYPTION_MASTER_KEY.
The relevant code:
| File | Role |
|---|---|
apps/frontend/src/utils/simConfig.ts |
Resolves and decrypts per-project credentials; caches briefly; validates the base URL |
apps/frontend/src/utils/simDiscovery.ts |
Lists a workspace's deployed workflows and turns them into LLM tool definitions |
apps/frontend/src/pages/api/UIUC-api/getSimWorkflows.ts |
Tools-page listing endpoint |
apps/frontend/src/pages/api/UIUC-api/runSimWorkflow.ts |
Executes a workflow |
apps/frontend/src/components/UIUC-Components/SimPage.tsx |
The project Tools page |
Five things worth knowing before you change any of it:
- Discovery is deployed-only. A workflow that exists but is not deployed is invisible to Illinois Chat. A workflow whose inputs have no descriptions is skipped and reported, rather than published with a guessed signature.
- Only
workflow.descriptionis read. Sim's v1 API (/api/v1/workflowsand/api/v1/workflows/{id}) exposes that one column; in Sim's UI it is written from Deploy → API → Edit API Info. The deployment-version, MCP-tool and chat descriptions live in other tables and are not exposed to API keys. When the column is empty,getUIUCToolFromSimsynthesises a fallback from the name and input fields and setshasAuthoredDescription: false, which the Tools page flags. - Execution is authorised against the same listing, so a hand-crafted workflow ID cannot spend a project's key on an undeployed or unrelated workflow.
- Reserved control fields are stripped from both advertised schemas and model-generated input, so the model cannot reach Sim execution internals through tool arguments.
- Outbound Sim URLs are allowlisted (an SSRF guard): sim.ai hosts, local hosts, the
origin of
SIM_API_BASE_URL, and anything inSIM_ALLOWED_SIM_ORIGINS. Project admins choose from that set but cannot add to it.
4. Who can get in¶
Two independent gates:
- Keycloak SSO, restricted to the single domain in
SIM_SSO_DOMAIN. Sim shares the app's realm, so there is no separate Sim password. - A per-user approval gate in Sim's database (
infra/docker/sim/approval-setup.sql). Every first sign-in lands inpendinguntil a platform admin approves it. Blocking revokes live sessions immediately.
The gate lives in the database so that the upstream images can be used unmodified.
Approving users¶
A sim_user_approval table stores one decision per email (pending, approved, or
blocked), and database triggers enforce it on Sim's user and session tables. The
bootstrap platform admin is the address in SIM_APPROVAL_ADMIN_EMAIL (required in
.env; the Sim stack refuses to start without it).
Admins have two equivalent ways to act on a request:
- In Sim's UI: sign in as a platform admin and open Settings → Admin. Pending users appear as banned; use Unban to approve them. Banning a user blocks them again. Actions taken here are mirrored into the approval table automatically.
- In the database: update the row directly, e.g.
UPDATE sim_user_approval SET status = 'approved' WHERE email = 'someone@example.edu';Valid statuses areapproved,pending, andblocked.
Decisions take effect immediately: approving unlocks the account on the next sign-in,
and blocking revokes the user's live Sim sessions on the spot. Setting is_admin = true
on a row promotes that user to Sim platform admin.
What users see while they wait, and how they connect a workspace once approved, is in the user guide.
5. The block whitelist¶
ALLOWED_INTEGRATIONS restricts which block types users may place in a workflow. Its value
comes from ALLOWED_INTEGRATIONS in .env, which is where the list lives and the only
place it lives; .env.template ships the full 67 ids. infra/docker/docker-compose.sim.yaml
passes it through with :? and no default, so a missing or blank value stops the Sim stack
instead of silently unrestricting it. The allowed set is documented for builders in the
user guide under Which Sim blocks you can use.
Six properties, all verified against the source at the pinned commit. Do not assume the public documentation applies — it describes a newer release that behaves differently:
- It works on its own. No
ACCESS_CONTROL_ENABLED, organization, or enterprise plan. - Matching is an exact lowercased string compare with no version resolution.
slackdoes not permitslack_v2. Write ids exactly as the registry spells them. - Core blocks are not exempt. Only the start trigger and hidden legacy blocks are. Omit
agentorfunctionand every workflow fails at its first block. - Enforcement happens at execution, not at save, so a workflow containing a denied block can still be saved or imported and fails only on a run.
- A denied vendor attached as an agent tool is dropped silently. The error is swallowed; the agent runs without that tool and nobody sees an error. Expect this as a support report, not a crash.
- A value Sim receives as empty means unrestricted, so it fails open. This is why the
compose key uses
${ALLOWED_INTEGRATIONS:?…}rather than a default: blanking the variable, or deleting the compose key, would otherwise remove the restriction with no visible sign. Changing that:?back to:-reintroduces exactly that hazard. - The value replaces the list wholesale — there is no merge. Setting the variable to a
single id (say
google) does not add that block; it denies everything else, core blocks included. Allowing something new means appending its id to the existing line.
Changing the list¶
Edit ALLOWED_INTEGRATIONS in .env and recreate the simstudio service. Update
.env.template in the same commit — it is the copy every new deployment starts from, and the
two drifting apart is the failure this layout exists to prevent.
Before committing, confirm the value has no duplicates and the expected field count, and that every id exists in the running image's block registry:
# field count and duplicates
awk -F'"' '/^ALLOWED_INTEGRATIONS=/{n=split($2,a,","); print n" ids"}' .env
awk -F'"' '/^ALLOWED_INTEGRATIONS=/{split($2,a,","); for (i in a) print a[i]}' .env | sort | uniq -d
# every id exists as a block type in the pinned image
docker run --rm --entrypoint sh <sim-image-id> -c \
'grep -rhoE "type:\"[a-z0-9_]+\"" /app | sed -E "s/type:\"(.*)\"/\1/" | sort -u'
There is no rollback-by-commenting-out any more: the stack will not start without the variable. To genuinely lift the restriction you must set it to every id you want allowed.
Two things this variable cannot do: disable MCP tools or custom tools. Those are
permission-group settings with no environment equivalent. ALLOWED_MCP_DOMAINS restricts
which hosts an MCP server may point at; the repository ships with none set.
6. Upgrading Sim¶
- Resolve the new digest:
docker buildx imagetools inspect ghcr.io/simstudioai/simstudio:latest - Set
SIM_APP_IMAGEand the matchingSIM_REALTIME_IMAGE/SIM_MIGRATIONS_IMAGE. - Re-check the approval gate. It patches Sim's
usertable; an upstream schema change could break or bypass it. - Re-check the whitelist. Every id must still exist in the new image. More subtly, upstream later resolves block versions to their successors, which would make an allowed id silently permit newer variants — widening the list without anyone editing it.
- Re-read the block registry and the access-control helpers if either check is unclear.
Treat an upgrade as a reviewed change, not a routine bump.
7. Troubleshooting¶
stop-dev.sh fails with "required variable SIM_… is missing a value". Compose
interpolates the whole merged file even for down, and the Sim services mark their secrets
as required. Your .env lacks them. Use stop-dev.sh --no-sim, which skips the Sim overlay
entirely.
Every SSO sign-in is refused. Check SIM_SSO_DOMAIN is one domain, not a list.
The Tools page says a stored key "could not be read". ENCRYPTION_MASTER_KEY changed
since the key was saved, or a migration is missing. Paste the key again. Project admins
are told to report this to you if it recurs, so check which of the two happened.
Saving a Base URL on the Tools page is rejected. The URL is not sim.ai, a local host,
the origin of SIM_API_BASE_URL, or listed in SIM_ALLOWED_SIM_ORIGINS (section 3).
Project admins cannot extend that set; add the origin to SIM_ALLOWED_SIM_ORIGINS.
A Connect button opens and closes immediately. That integration needs a deployment-wide OAuth client that this deployment has not registered. This is expected; see the user guide.
An agent quietly stopped using a tool. Most likely that vendor is not on the whitelist.
Check the container log for Integration blocked by env allowlist alongside
[AgentHandler] Error creating tool.
8. Sim's own documentation, and how far to trust it¶
Start here for anything not covered above. Sim's published docs track their latest
release; this deployment runs the pinned v0.8.4 (commit e741923f). Each row links
the page as it was for the pinned version (the documentation source at that commit, viewed on
GitHub) and the current page.
| Topic | Why it matters here | Pinned version (v0.8.4) | Current docs |
|---|---|---|---|
| Environment variables | The full reference for the SIM_* values passed through by the compose file. |
v0.8.4 | current |
| Integrations and OAuth | Which providers need which *_CLIENT_ID / *_CLIENT_SECRET pairs, and the callback URL shape. Read before registering any vendor application. |
v0.8.4 | current |
| Security and hardening | The SSRF boundary, egress allowlists, and where user code runs. | v0.8.4 | current |
| Authentication | Signup restrictions and SSO options. | v0.8.4 | current |
| Access control | Permission groups, and the only documentation of ALLOWED_INTEGRATIONS. |
v0.8.4 | current |
| Troubleshooting | Upstream's own list, for anything not in section 7. | v0.8.4 | current |
The whole documentation set for the pinned version is browsable at
apps/docs/content/docs/en at e741923f.
The current docs also have a Sandboxes
page that did not exist at the pinned version; it is still relevant because this deployment runs
the default in-process sandbox: Function block code executes inside the app container
with no network or filesystem separation. Anyone who can author a workflow runs code in
that container's security context. Accepted for now given the approval gate.
Treat the current pages as indicative, not authoritative for this deployment. During this work several documented behaviours turned out to differ from what the pinned version actually does — including how the block allowlist matches ids and which blocks it exempts. When a detail matters, prefer the v0.8.4 column, and when even that is unclear, read the source at the pinned commit:
The block registry (apps/sim/blocks/), the environment flag parsing
(apps/sim/lib/core/config/) and the executor (apps/sim/executor/) are the three places
worth knowing how to find.