Waleed 6bbabbbac6 fix(cli): close follow-up gaps (#7137)
* chore: run the orphaned migration-safety test, and stop publishing real-looking ids

One of the eight script tests was reachable from no entrypoint, so it had
never run in CI — it passes, it was simply never invoked. This is the second
time that hand-maintained list has drifted from the files beside it; the
audit runner's own header records the first. The guard against a third is a
`check:*` script rather than a test, because the runner derives its list
from that namespace and so picks the guard up by name — a test would have
had to be hand-added to the very list it guards.

A published spec sat outside the generator's manifest and so outside its
drift check, and carried six example ids with the texture of real generated
ones rather than the pandigital placeholders the rest of the repo uses. The
new check globs the directory instead of reading the manifest, since the
manifest is what omitted the file. The one pre-existing borderline id is
allowlisted with a reason: loosening the threshold to admit it would have
hidden one of the six.

* fix(cli): close the gaps black-box testing the shipped CLI found

`sim profiles <anything>` still exited 0, so a probe reading the exit code
to ask whether a command exists was told yes — the one group the earlier
guard missed. The exemption was written for commands that are both a group
and a leaf, but only `files restore` takes an operand; `profiles` takes
none. Registering its listing as a default subcommand puts it back among
the pure dispatchers the existing guard already covers, so the guard itself
did not need widening.

Three commands refuse a workspace API key and said nothing, while their
menu siblings said so — reading as though they accept one. They are
hand-written, so they never reached the code that appends the note. That
note now comes from a helper taking the operation, so a command names the
operation it invokes and the two cannot disagree, and a test fails if a
hand-written command ever calls a restricted operation without it.

A blank numeric value in a request body still became a real zero, the same
coercion already fixed for query strings: the guard keyed off the slot when
the distinction is the field's declared type. Twenty-one fields across
fifteen operations were affected. An empty body string still clears a
description.

Blank values for the root endpoint, workspace and profile flags fell back
to what was configured instead of being refused, and a whitespace workspace
was accepted verbatim. A hand-written profile name carrying padding listed
as reachable but resolved to defaults rather than erroring. Two schema
descriptions named request fields that no flag spells, and a rejected value
was echoed unredacted by four messages while their siblings redacted it.

A write now re-emits a section header it was not asked to touch byte for
byte. The blank-line normalisation around it is left alone: making the
writer position-faithful is a change to its model, not a fix.

* fix(scripts): match example uuids case-insensitively in the spec audit

The pattern only recognised lowercase hex, so an uppercase id in a
published spec was never examined and the audit reported success without
having looked at it.

Matching case-insensitively is not enough on its own: hex is
case-insensitive, so a mixed-case id counts `A` and `a` as two digits and
reports twenty distinct ones rather than sixteen. That inflated count
clears the threshold the texture test uses to recognise a hand-authored
placeholder, so a real id could have passed for one. The allowlist is an
exact-string lookup and would likewise have missed an uppercase spelling of
an entry. Both checks and the lookup now take a normalised id, while the
finding still reports the spelling as it appears in the file.

* fix(cli): stop a refusal being swallowed, and gate example ids by name

A blank root flag was refused everywhere except `profiles`, where the catch
that lets a broken profile still list absorbed it and the command exited 0
after printing the table. The refusal now carries its own error class, which
is what the listing rethrows on — the two are distinguished by type rather
than by matching message text, and a genuinely broken profile still lists.

The unknown-profile message redacted the name the caller typed but not the
suggestion or the list of configured names beside it, which come from the
same file and are equally attacker-influenced once it has been hand-edited.
Those are redacted now, as is every other message in these two files that
quotes a name read out of the config, and the profile listing flattens the
names it renders the way it already flattened the error column.

The example-id audit judged a uuid by its digit texture, on the premise that
a real one essentially never looks hand-authored. Measured against ten
million generated ids, 0.81% of them do — one in 124, where this change
alone replaced six. Requiring each digit exactly twice takes that to zero
but rejects all fourteen placeholders now in the specs, so it is no cheaper
than the alternative. The audit now holds the eighteen ids the specs
actually use, which is one file rather than the twenty-seven a reserved
format would touch, and a new id fails until someone lists it — which is the
review the check exists to force.

* fix(scripts): match the uuid sentinels exactly rather than by shape

Accepting any id built from at most two distinct hex digits let something
through that was never on the approved list. A generated id essentially
never has that shape, so the practical risk was small — but this check had
just stopped being a shape test and become a list, and a structural
exception is the one thing that undoes that. The two ids it exists for are
the nil and max sentinels, and both are matched by value now.
2026-08-26 17:53:39 -07:00

Sim.ai Documentation Slack X

Ask DeepWiki Set Up with Cursor

Sim — Integrate, Context, Build, and Monitor AI agents

A workspace to build, deploy and manage AI agents and workflows.

Quickstart

Cloud-hosted: sim.ai

Open sim.ai

Self-hosted

npx sim-setup

Open http://localhost:3000

The Sim platform — chat on the left, the visual workflow builder on the right

Capabilities

  • Connect 1,000+ integrations and every major LLM
  • Add Slack, Notion, HubSpot, Salesforce, databases, and more
  • Build agents visually, conversationally, or with code
  • Ingest files, knowledge bases, and structured table data
  • Monitor runs, logs, schedules, and workflow activity

One workspace, every surface

Chat and workflows are just the start — tables, files, and knowledge all live in the same workspace.

Tables in Sim — structured data your agents can query

Tables — a database, built in

Files in Sim — documents for your team and every agent

Files — one store for your team and every agent

Knowledge bases in Sim — synced docs your agents can search

Knowledge — your agents' memory

Self-hosting

Requirements: Node.js 20+ and Docker.

npx sim-setup is an interactive wizard that creates a small sim/ deployment directory, provisions the database, generates secrets, writes .env, connects a Chat API key, and starts the published Sim images with Docker Compose. It does not clone the repository.

When it finishes, open http://localhost:3000.

Inside a cloned Sim repository, run bun run sim-setup to unlock the source-only local development and Kubernetes modes.

Reconfigure an optional capability without rerunning the full wizard:

npx sim-setup config
npx sim-setup add email
npx sim-setup add storage
npx sim-setup add sandbox
npx sim-setup add jobs
npx sim-setup add cache
npx sim-setup add knowledge
npx sim-setup add chat
npx sim-setup add llm
npx sim-setup add integration slack

npx sim-setup config detects the effective local-dev, Docker Compose, or current-context Helm configuration and reports configured, missing, or invalid capabilities and OAuth integrations without printing credential values. This is separate from npx sim-setup status, which reports whether installed services are running and healthy.

Manage your install from its directory:

npx sim-setup start | stop | restart   # bring your install up / down / cycle
npx sim-setup update                   # pull and apply Compose images
npx sim-setup status                   # what's installed and healthy
npx sim-setup logs                     # follow logs
npx sim-setup doctor                   # diagnose configuration problems
npx sim-setup down                     # remove containers (data kept)
npx sim-setup reset                    # archive .env and wipe managed data

The setup package detects how you're running and acts accordingly. Use --dir <path> to create or manage a deployment somewhere other than ./sim.

Sim also supports local models via Ollama and vLLM. See the self-hosting docs for details.

Chat API Keys

Chat is a Sim-managed service. npx sim-setup connects a Chat API key for you — sign in when it opens your browser and the key is stored automatically. To view, create, or revoke keys later, go to sim.ai/selfhost/settings/chat-keys.

Environment Variables

See the environment variables reference for the full list, or apps/sim/.env.example for defaults.

Tech Stack

Next.js · Bun · PostgreSQL · Drizzle · Better Auth · Tailwind — and the rest of the stack

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Built by the Sim team in San Francisco

Languages
TypeScript 77%
MDX 20.8%
JavaScript 1.9%
CSS 0.1%