synsema

← Docs

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 (and domains of your own), secrets kept out of your repository, logs, the audit of every capability check, schedules for jobs, the human gates of your program (approve, confirm, ask) answered from a dashboard or an API, and tunnels that put a port of your own computer at a fixed HTTPS address.

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, once for the whole computer. No password goes through the terminal. The token stays in ~/.synsema/syn.json, under the computer's name (--name, or its host name). Each project folder only keeps its project id in .synsema/syn.json — add .synsema/ to your .gitignore.

In CI, or on a server without a browser, skip the login: set SYN_TOKEN (or SYNSEMA_TOKEN) to 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, named after the folder (--name picks another; a syn.toml can name it too); after that it sends a new version. It prints the ceiling — each require line of your program, granted as declared on every plan — and any secret the program still needs. What the program did not declare, the runtime denies on every call. What can stop a deploy is a resource the plan does not hold (memory per service, services at once): the platform says so 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.

Without a terminal, the dashboard's New project takes the same folder from the browser (drop it or choose it; .env is never uploaded), a public GitHub repository, or a recipe.

Commands§

CommandWhat it does
syn signup / syn loginSign this computer in from the browser (above)
syn whoamiThe site, account and computer syn uses here, and where the token comes from
syn deploy [--name N] [--entry E] [--kind web|worker]Upload the folder and queue a deploy
syn statusState, 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 projectsEvery project of the account
syn openThe URL of this project
syn filesWhat would travel, without uploading
syn stopTake the service down; files, secrets and volume stay
syn delete --yesRemove the project and everything the runner holds for it
syn tunnel <port> [--name N] [--subdomain S] [--json]A port of this computer at a fixed HTTPS address (Tunnels, below)
syn tunnel list · syn tunnel release <name>The tunnels of the account; give an address up
syn tunnel <port> --name N --service · syn tunnel service list|remove <name>Keep a tunnel running with the computer, without a terminal open
syn agent …Agents that talk to each other (REDSYN)
syn updateThis CLI again, from the site it signs in to (the engine updates apart: synsema update)

A command that fails ends with exit code 1, so a script or an agent can tell.

What a project is§

A folder of Synsema files with an entry file. Its require block is the manifest: the platform grants every line as declared, on every plan, and the runtime denies anything the program did not declare. 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.

KindRunsURL
websynsema 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>
workersynsema run <entry>, always onnone (a serve on block is served on a loopback port, so its human gates work)
jobsynsema run <entry> in a fresh container per run, on a schedule or when askednone

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"

Domains§

A web project answers at <slug>.synsema.app. The slug is free to choose and to rename later; extra names are labels, <slug>-<label>.synsema.app. On Pro and Enterprise it can also answer at a domain of your own: add it on the project page, then point your DNS at the platform:

Your nameDNS record
a subdomain, app.example.comCNAME to <slug>.synsema.app
the root, example.comA to the IP address the project page shows (the root of a domain cannot be a CNAME; a DNS provider with CNAME flattening or ALIAS works too)

Add www.example.com as one more name if you want it too. The HTTPS certificate is issued the first time the name is visited. Every name reaches the same program, which tells them apart by host; a name added or removed takes effect on the next deploy.

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.

On Pro and Enterprise, the Approvals page can turn on push notifications in each browser you use: the question reaches your computer or phone the moment the program asks, even with the tab closed (on iPhone, once the site is added to the home screen).

Jobs, schedules and sleep§

  • A job runs from its schedule ([schedule] or PUT /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, and a service kept warm still sleeps after 7 days without a single request. A worker never sleeps.

  • What nobody uses goes. On Free, a project with no requests, deploys or runs for 7 days is paused

(it stops; its code and data stay) and deleted 30 days later unless it is deployed again. On a paid plan nothing is deleted for want of use; when an account stops paying, what the Free plan cannot hold is deleted 30 days later. Every deletion date is announced by email a week and a day before.

The audit§

Every capability check of a running program — what it asked for, granted or denied, and why — is kept in the project's audit, never what flowed through it. How long depends on the plan: the last 7 days on Free, 90 days on Pro, without limit on Enterprise. On Pro and Enterprise the audit exports as CSV or JSON Lines, from the project's Audit tab or from the API:

curl "https://synsema.com/api/v1/projects/<id>/audit/export?format=csv" \
  -H "Authorization: Bearer $SYN_TOKEN" -o audit.csv

format is csv or jsonl; only=granted|denied and q=<text> filter like the dashboard.

Tunnels (syn tunnel)§

syn tunnel <port> puts 127.0.0.1:<port> of your computer at a fixed https://<id>-tunnel.synsema.app, without opening ports or touching DNS: the computer opens one outbound connection and every request comes through it — HTTP with bodies of any size, Server-Sent Events as they are emitted, WebSocket both ways. It only forwards to that port of that computer.

syn tunnel <port>                                   # https://<id>-tunnel.synsema.app → 127.0.0.1:<port>
syn tunnel <port> --name <name>                     # another tunnel on the same computer, its own address
syn tunnel <port> --name <name> --subdomain <sub>   # Pro: https://<sub>.synsema.app as well
  • The address is fixed for the computer and the --name (default default): the same every

time, kept by the account. syn tunnel release <name> gives it up.

  • --subdomain (Pro and Enterprise) gives the tunnel a name of its own under synsema.app, in the

same namespace as the projects' URL names. The name is checked before anything is created: same rules as a project's slug, not reserved, not ending in -tunnel, and free — a name belongs to one project or one tunnel. A name that cannot be had is refused with the reason (… is already in use, that name is reserved) and nothing changes; on Free it is refused with code: plan. Change it by running again with another --subdomain, or from Tunnels on the dashboard, which also removes it. The <id>-tunnel address keeps answering.

  • What the local service receives: the public Host, and always X-Forwarded-For (the

visitor's IP), X-Forwarded-Proto: https and X-Forwarded-Host. A Synsema service sees them only if it trusts the tunnel, which reaches it over loopback: trust proxy ["127.0.0.1"] in its serve block, or --trust-proxy 127.0.0.1 (Server).

  • When the computer is not connected, the address answers 503 with X-Synsema-Tunnel: offline.
  • Limits per plan: Free 3 addresses and 5 GB a month, with a warning page the first time a

browser opens a tunnel (API calls, fetch and WebSocket never see it); Pro 20 addresses and 100 GB, no warning page; Enterprise 100 addresses and 1 TB.

  • Kept running: syn tunnel lives while its terminal does. Add --service and the computer keeps

it running instead: it starts with the computer and needs no terminal open — a systemd unit on Linux (a user unit when you are not root), a LaunchAgent on macOS, the Startup folder on Windows. syn tunnel service list shows them with their last state; syn tunnel service remove <name> stops one (the address stays yours). Its log is ~/.synsema/tunnels/<name>.log.

``sh syn tunnel <port> --name <name> --service ``

  • For programs that start a tunnel: --json prints one JSON line per event —

{"event":"connected","id":…,"id_url":…,"name":…,"port":…,"url":…}, reconnecting with retry_in, and error with fatal, code and message (a fatal error is the last line, and the command exits with 1). It reconnects by itself with backoff; Ctrl-C stops it.

Agents that talk to each other§

The platform also relays messages between agents, across machines and companies: syn agent listen and syn agent send. The protocol, with its HTTP API, signatures and end-to-end encryption, is REDSYN.

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.