---
slug: 74-platform
title: Synsema platform (syn)
description: The hosted way to run Synsema — the syn CLI (install, signup and login from the terminal, deploy, logs, secrets, env), what a project is (web, worker, job), syn.toml, the ceiling your require block sets against the plan, human gates, schedules, sleep and the HTTP API.
example_ids: []
---

# Synsema platform (`syn`)

[synsema.com](https://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](71-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](00-quickstart)):

```sh
curl -fsSL https://synsema.com/cli/install.sh | sh      # macOS, Linux → ~/.local/bin/syn
```

```powershell
irm https://synsema.com/cli/install.ps1 | iex           # Windows → %LOCALAPPDATA%\Synsema\syn
```

## Account and login

```sh
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:

```sh
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.

```toml
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]` 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. A
  **worker** never sleeps.

## The API

Everything `syn` and the dashboard do is an HTTPS API with a bearer token:
[synsema.com/docs](https://synsema.com/docs) and the machine-readable
[openapi.json](https://synsema.com/openapi.json).
