Commands
Everything spry does. spry --help prints a shorter version of this.
npm install -g spryloom
Where a command takes an app, it takes the app's slug — the first part of its
address, and the app.slug in its manifest. spry apps lists them.
Signing in
spry login --email you@yourcompany.com
Signs this terminal in. Prints a short code and waits; Spryloom emails you a link, and the page it opens shows the same code. Confirm it and the terminal finishes.
Check the code. A link on its own approves whichever sign-in it was made for, including one somebody else started.
spry whoami
Who this terminal is signed in as, and which workspace.
spry logout
Signs this terminal out. Its token is revoked, so a copy of it stops working too, and other machines are unaffected. If Spryloom cannot be reached, you stay signed in, so running it again finishes the job.
spry logout --all
Signs out everywhere, this terminal included, by revoking every token you hold. What you reach for when a laptop is lost.
Publishing
spry publish [folder]
Publishes the app in the folder. Defaults to the current directory.
Also takes a zip of the folder, or a GitHub repository:
spry publish ./expense-notes.zip
spry publish github.com/you/expense-notes
spry publish github.com/you/expense-notes@a-branch # also #a-branch
Both are read on your machine and become a folder before anything is uploaded.
What is uploaded leaves out node_modules, .git, anything starting .env,
credential files, and framework build output Spryloom makes again when it
builds (.next, .nuxt, .svelte-kit, .turbo, .parcel-cache, .vercel).
dist and build are kept, since a static app may be exactly that.
For a private repository set GITHUB_TOKEN; it is sent to GitHub and nowhere
else.
| Option | |
|---|---|
--description <text> |
Required when the folder has no spryloom.yaml. |
--name <text> |
What to call the app. Defaults to the folder, the repository, or the zip. |
Publishing again keeps the address, the data, and the database, and raises the version number.
While it runs, spry shows each stage as it happens (preparing, database,
building, starting, health check, jobs, switching over), with a timer and the
latest line of the build. The work happens on Spryloom, not on your machine:
Ctrl-C stops watching and the publish carries on, and spry apps shows how it
ended. Publishing again while one is running watches that one rather than
starting a second. The version that was live stays live until the new one
answers.
spry delete <app> <app>
Removes the app, its machine, its database and the data in it, its secrets and its custom domains. The name twice, because none of it comes back. It shows its stages like a publish, and says it is done only after checking that each of those is gone; if one is not, it names it and you can run it again.
spry rollback <app> [--to N]
Returns the app to an earlier version — the previous one, or the one you name.
The version being replaced is retired only once the earlier one is answering.
Like a publish, it shows its stages while it runs, and Ctrl-C stops watching
while the rollback carries on. spry restore and spry restart do the same.
Your data is untouched. Rolling back changes which code is running, nothing else.
spry restart <app>
Starts an app that has stopped, using the version already there. What you reach for when an app has run out of memory or crashed.
Do not publish again to recover from a crash: the code has not changed, and rebuilding risks a different result from a dependency that moved since.
spry archive <app>
Stops serving an app while retaining its database and data. Use this for a
finished project or a temporary pause. Restoring an archived app is currently a
available with spry restore.
spry restore <app>
Starts the last published version of an archived app while retaining its database and data. It does not rebuild the app or change its schema.
spry delete <app> <app>
Removes an app and everything made for it: the machine it ran on, its database and the data in it, its secrets, and its record. It tells you what went.
The name goes in twice. That is not politeness, it is the confirmation: this is the one command that destroys something nobody can get back, and typing the name again is how you say you meant this app rather than the one next to it in the list.
spry delete expense-notes expense-notes
If you only want it to stop costing you anything, you do not need this. An app that nobody is using is asleep, and asleep costs close to nothing: no machine is running and its database is suspended. Delete is for when the data should be gone, not for tidying up.
Sharing
spry invite <app> --email someone@yourcompany.com
Lets somebody use an app. Repeat --email for several people.
Anybody who cannot use the app is not invited and is not emailed, and is listed with the reason. Inviting somebody who already has access is not an error.
If the app is private, nobody can be invited to it at all, so the whole
command is refused and says which setting to change. Nothing is sent and nobody
is recorded as invited.
You can also share from the browser dashboard's Invite a coworker form.
spry uninvite <app> --email someone@yourcompany.com
Removes an invited address from the app. They cannot start a new session after
the removal; an already-open session ends at the documented session boundary.
The owner or a named app admin must perform the removal. The dashboard and the
MCP uninvite tool use the same operation.
Your own hostname
An app answers at <app>.<workspace>.spryloom.app from the moment it is
published. It can also answer at a hostname on a domain you control.
spry domain add <app> <hostname>
Prints two DNS records to add at your registrar: a TXT record that proves
you control the hostname, and a CNAME pointing the hostname at Spryloom.
Add them in either order. Nothing is served and no certificate is requested
until the proof is in place.
A hostname on a domain Spryloom keeps for itself (spryloom.com,
spryloom.app) is refused, whatever the app.
If your DNS is at Cloudflare, set both records to DNS only (the grey
cloud), not proxied. A proxied hostname has Cloudflare answer the connection
in front of Spryloom, so the certificate is never issued and the hostname
never comes up. The TXT record is unaffected either way.
spry domain verify <app> <hostname>
Checks the TXT record. DNS can take a few minutes to catch up; if it is not
there yet the command says which record it looked for, and you run it again.
Once it succeeds, the hostname serves the app and a certificate is issued on
the first request. Sign-in, the label page and everything else work exactly as
they do on the Spryloom address, and a session on one hostname is worth
nothing on any other app.
spry domain list <app> and spry domain remove <app> <hostname>
What is attached, and whether it is verified; and taking one away, after which the hostname serves nothing.
Only the app's owner or an admin can add or remove a domain.
Data
spry export <app>
Prints a JSON export of the app's platform metadata, manifest, audience, versions, and safe audit history. Redirect it to a file before leaving Spryloom:
spry export expense-notes > expense-notes-spryloom-export.json
Application database rows are not read by Spryloom. Export those from the app itself; the JSON bundle says this explicitly.
The MCP export tool returns the same JSON bundle to a coding agent.
spry data <page>
A page's saved lists: each one's records and who sees them, and how much of the page's 50 MB is used.
spry data export <page>
Saves every record of a page into <page>-data/, as JSON and CSV for each list,
lists the page no longer declares included. For the page's owner and admins, and
recorded in the audit log. A CSV cell that a spreadsheet would run as a formula is
written as text.
spry secrets <app>
What is set on the app, and when. Never the values — nothing can read one back.
spry secrets <app> set NAME=value
Gives the app a value nobody can read back. Repeat for several.
Secrets reach the app when it is published, so publish again afterwards. An app cannot be given one before it exists.
spry secrets <app> remove NAME
Removes it. Publish again so the running app stops seeing it.
Looking
spry apps
Every app in your workspace that you can open or look after: whether it is running, its address, how many people use it, who it is shared with, and which version. A colleague's app shows here only once it is shared with you.
Whether it is running is asked of the platform, not remembered — an app that has stopped says so.
spry logs <app> [--lines N]
What the app printed, newest last. Defaults to 100 lines, and 1000 is the most it will return whatever you ask for. Only the app's owner and admins can read it, because what an app prints can be the data it handles.
Lines the platform wrote rather than your app are marked [fly]. They are kept
because they are often the answer: an app killed for running out of memory
printed nothing about it, and that line is the only record of what happened.
spry logs <app> --job <job>
A scheduled job's recent runs, how each ended, and what the newest finished run printed.
Scheduled jobs
spry jobs <app>
Each job, when it runs in words, when it runs next, and how its last run went.
spry jobs run <app> <job>
Runs the job now, with the same code, data and limits as a scheduled run.
spry logs <app> --job <job> shows how it went.
spry jobs resume <app> <job>
Starts a job again after it was paused for failing 5 times in a row. Publishing the app again does the same.
Before you publish
spry check [folder]
Shows what Spryloom sees in the folder and whether it could publish it. Changes nothing. Worth running when a publish has failed and you want to know why before trying again.
spry init [folder] --description "what it does"
Writes a spryloom.yaml without publishing, so you can read and edit it first.
| Option | |
|---|---|
--description <text> |
One sentence your coworkers will read. Required, unless package.json has a description to take it from. |
--name <text> |
App name. Defaults to package.json, then the folder. |
--visibility <who> |
private, invited, company, or link. Defaults to private. |
--domain <domain> |
Email domain. Required when visibility is company. |
--admin <email> |
Grant admin. Repeatable. |
--table <name> |
A table the app stores. Repeatable. Shown to your coworkers. |
--no-database |
This app stores nothing, whatever its dependencies suggest. |
--force |
Overwrite an existing spryloom.yaml. |
From your coding agent
spry skill [folder]
Writes the Spryloom skill into .claude/skills/spryloom/, so the agent working
in this project writes apps that publish: listening on the port it is given,
committing a lockfile, declaring a manifest.
This is where a first-attempt publish is won or lost. Every failure the platform can report is something the skill teaches an agent to avoid, and the two are kept in step by a test.
It will not overwrite a skill that is already there, since yours may have been
edited. --force if you mean it.
Codex and Cursor have no skill format. They get the same guidance from the tool descriptions, which needs nothing written to disk.
spry mcp
Serves the agent tools on standard input and output. You do not run this yourself; your agent starts it.
claude mcp add spryloom -- spry mcp
It acts as whoever this terminal is signed in as. When nobody is, it still
starts, and the agent signs you in with the sign_in and finish_sign_in
tools the first time it needs to. See using Spryloom from your agent.
Everywhere
| Option | |
|---|---|
--no-color |
Plain output, for a log or a pipe. |
-h, --help |
The short version of this page. |
-v, --version |
Which spry this is. |
| Environment | |
|---|---|
SPRYLOOM_URL |
Which Spryloom to talk to. |
SPRYLOOM_TOKEN |
A token to use instead of the signed-in one, for continuous integration. |
SPRYLOOM_CONFIG |
Where the session file lives. |
GITHUB_TOKEN |
For publishing from a private repository. Sent to GitHub only. |
The tools your agent gets
The same things, as tools: publish, status, logs, rollback, invite,
uninvite.
Ask your agent to publish and it will use them.