Operate
Synsema platform (syn)
synsema.com runs Synsema programs for you: a folder goes up, a URL comes back. It is one way to deploy — the same program runs on your own server with synsema serve (Deploy); nothing in the language depends on the platform.
What the platform adds: containers, a URL with HTTPS, secrets kept out of your repository, logs, the audit of every capability check, schedules for jobs, and the human gates of your program (approve, confirm, ask) answered from a dashboard or an API.
Install syn§
syn is itself a Synsema program, so the engine goes first (Quickstart):
curl -fsSL https://synsema.com/cli/install.sh | sh # macOS, Linux → ~/.local/bin/syn
irm https://synsema.com/cli/install.ps1 | iex # Windows → %LOCALAPPDATA%\Synsema\syn
Account and login§
syn signup # no account yet
syn login # already have one
Both print a page and a short code. Open the page, create the account there if you need one, and approve the code: the terminal signs in. No password goes through the terminal. The token stays in .synsema/syn.json in the current folder — add .synsema/ to your .gitignore.
In CI, skip the login: set SYN_TOKEN (an API token from Settings on synsema.com) and, if you use another site, SYN_SITE.
Deploy§
From the folder of your project:
syn deploy
The first time it creates the project; after that it sends a new version. It prints the ceiling — each require line of your program, granted or denied by your plan — and any secret the program still needs. A program that asks for more than the plan allows does not deploy: the platform refuses it before it runs, instead of letting it fail later.
What travels: every file of the folder except what .gitignore names, plus .git, .env, .synsema, node_modules and target, which are always left out. A file over 1 MB is skipped with a warning; the whole package must stay under 8 MB. syn files shows the list without uploading.
The entry file is the one syn.toml names; without it, the single top-level .syn with a serve on block, or --entry.
Commands§
| Command | What it does |
|---|---|
syn signup / syn login | Sign this folder in from the browser (above) |
syn deploy [--name N] [--entry E] [--kind web|worker] | Upload the folder and queue a deploy |
syn status | State, package, ceiling and deploys of this folder's project |
syn logs [--follow] | What the platform and the runner wrote |
syn secrets [set NAME=value …] | List the secrets set and the ones still needed, or set them (write-only) |
syn env [set NAME=value … [--secret]] [rm NAME …] | The environment: list it as .env, set, remove |
syn projects | Every project of the account |
syn open | The URL of this project |
syn files | What would travel, without uploading |
syn stop | Take the service down; files, secrets and volume stay |
syn delete --yes | Remove the project and everything the runner holds for it |
What a project is§
A folder of Synsema files with an entry file. Its require block is the manifest: the platform grants what the plan allows and refuses anything above it. A deploy runs the program in a container under that ceiling with the audit on, so every capability check lands in the project's audit.
| Kind | Runs | URL |
|---|---|---|
web | synsema serve <entry> | <slug>.synsema.app, extra names <slug>-<label>.synsema.app, your own domain on Pro. The program gets them as SYNSEMA_HOST, SYNSEMA_HOSTS, SYNSEMA_HOST_<LABEL> |
worker | synsema run <entry>, always on | none (a serve on block is served on a loopback port, so its human gates work) |
job | synsema run <entry> in a fresh container per run, on a schedule or when asked | none |
syn.toml§
Optional, at the root of the folder. It says what the code cannot say about itself; the require block stays the manifest.
name = "Invoice agent"
slug = "invoices" # the URL name; free to change later
entry = "app.syn"
kind = "web" # web | worker | job
memory = "512m" # optional; the plan has a default and a ceiling
[schedule] # jobs only, UTC: five cron fields or @hourly, @daily, …
cron = "0 * * * *"
[secrets] # what the program needs set, and a line for whoever sets it
LLM_API_KEY = "the key"
[env] # variables with a default, shown in clear, editable
LLM_PROVIDER = "anthropic"
[hosts] # web only: extra names; label = what it is for
api = "the JSON API"
[volumes] # folders the program writes into, kept between deploys
data = "./data"
[provision] # a URL the platform asks once, at creation; its env seeds the environment
env_url = "https://example.com/token"
Human gates§
A program that reaches approve "Pay 800 USDC to the vendor?" within 2h (or confirm, or ask … with [...]) stops at that line under serve. The question appears in Approvals on synsema.com and in GET /api/v1/approvals; answer it there or with POST /api/v1/approvals/{id}, and the waiting request continues. Past within with no answer it counts as a no. Only the request that reached the gate waits; the rest of the service keeps answering. In a job, or a worker without serve on, nobody can answer, so approve answers no at once — an unattended program cannot grant itself the threshold.
Jobs, schedules and sleep§
- A job runs from its schedule (
[schedule]orPUT /api/v1/projects/{id}/schedule), from
POST /api/v1/projects/{id}/run, or from the dashboard. A run never overlaps the previous one and is given up after two hours; each keeps its exit code and the tail of its output.
- A web service nobody calls for a while sleeps and wakes on the next request in about a second
(the request waits). Free plans sleep after 10 idle minutes; Pro and Enterprise choose. A worker never sleeps.
The API§
Everything syn and the dashboard do is an HTTPS API with a bearer token: synsema.com/docs and the machine-readable openapi.json.