Docs menu

Your app's manifest

spryloom.yaml sits in your app's folder and says what the app is. Spryloom builds exactly what it declares.

It is a contract, not configuration. It lives in your repository, you can read and edit it by hand, and it is what your coworkers are shown — the description appears in their invitation email, and what the app stores appears on its page. Nothing about your app is inferred behind your back.

Your coding agent writes it while it builds the app. spry init writes one too, and spry publish writes one if there is none.


The whole thing

app:
  name: Expense Notes
  slug: expense-notes
  description: Tracks expenses that need a second look.

runtime:
  frontend: none
  backend: node
  start: node server.mjs

access:
  visibility: company
  domain: yourcompany.com
  signin: [email_link]
  admins: [priya@yourcompany.com]

data:
  postgres: true
  tables: [expenses, approvals]
  uploads: false

jobs:
  - name: weekly-digest
    command: node jobs/digest.js
    schedule: "0 9 * * 1"
    timezone: America/New_York

email: true

egress:
  - slack.com

jobs, email and egress are optional. Leave them out and the app does nothing on its own, emails nobody, and reaches nothing outside Spryloom.


app

Field
name What people call it. Shown everywhere a person sees the app.
slug The first part of its address, and what commands take. Lower case, letters, digits and hyphens, up to 40 characters. No double hyphen, and it cannot start xn--. Derived from the name if you leave it out.
description One sentence saying what the app does for the person using it. Required.

The description is the one field worth thinking about. Your coworkers read it before opening something a colleague made, and "internal tool" tells them nothing. Write what it does for them.

name is capped at 60 characters and description at 200. A handful of slugs are reserved because they would collide with a platform hostname or mislead somebody reading a URL: www, api, app, admin, docs, status, support, test and about thirty more. Choosing one is refused, and the refusal names it.

runtime

Field
frontend none, static, or react.
backend none or node.
start The command that starts the app. Required unless you supply a Dockerfile.
build A build command, when the app needs one. Optional.
dockerfile Path to your own Dockerfile. Optional — see below.

frontend and backend cannot both be none: there would be nothing to run.

Python is named in the plan and is not built. Today this is Node, with a static or React front end.

You do not need a Dockerfile. Spryloom writes one, correctly, from what is here. If your app already has one at its root it is used exactly as it is, and nothing is overwritten. If it lives anywhere else, name it:

runtime:
  dockerfile: docker/Dockerfile

The path is relative to the folder you publish, and it has to be inside it. A path that names a file which is not there is refused, rather than ignored in favour of a generated image you did not ask for.

access

Field
visibility private, invited, company, or link.
domain Email domain. Required when visibility is company.
signin How people sign in. [email_link] today.
admins Email addresses that get the admin role. Optional.

See who can use your app for what each of those means and how your app is told which role somebody has.

signin also accepts google, which does nothing yet — do not declare it. The app's page tells your coworkers how they will actually sign in, and naming a method that is not there would make that page wrong.

data

Field
postgres true gives the app a database of its own.
tables What the app keeps in it. Required when postgres is true, and refused when it is false.
uploads false. true is refused: file uploads are Release 2.

tables is what you tell people, not what Spryloom creates — your app creates its own tables. See data and secrets. It is not optional, in either direction: a database with no tables listed is refused, because your coworkers read that list on the app's label page before they open it, and so is a table list on an app with no database.

uploads: true is refused rather than ignored. Declaring something the platform does not do would put it on the label page, and that page is the one your coworkers are asked to believe.

jobs

Work the app does on its own, on a schedule. Each run is your app's own code, with its database, its secrets and the same limits as the app, started at the times you give and stopped when the command finishes.

Field
name Lower case letters, digits and hyphens. Unique within the app.
command What to run, the way you would type it in the app's folder: node jobs/remind.js.
schedule Five-field cron: minute, hour, day of the month, month, day of the week. "0 9 * * 1-5" is weekdays at 9:00.
timezone The time zone the schedule is read in, such as Europe/London. UTC if you leave it out.
timeout_minutes How long a run may take before it is stopped. 10 if you leave it out, 15 at most.

During the beta a job runs at most once every 10 hours, an app has at most 5, and a job that fails 5 times in a row is paused and you are emailed. See jobs and email.

email

true lets the app email the people who use it: a short notification with one button that opens a page in the app. Spryloom sends it and writes the email around it. At most 5 a day during the beta, and only to people invited to the app or who have signed in to it. See jobs and email.

egress

The outside services the app may reach, by host name, each from the list at outside APIs. HTTPS only. A host not on that list is refused when you publish, and the refusal says how to ask for it.

Each host is shown on the app's page before anyone signs in, in words: a coworker sees "Reads from and posts to Slack", not just a host name.


Editing it

Change it and publish again. Everything in it takes effect on the next publish: who can use the app, what it is called, what it declares that it stores.

Two things are worth knowing:

  • Changing the slug makes a new app, at a new address, with its own database. It does not rename the old one.
  • Removing postgres: true does not delete the database. The app stops being given a connection string; the data stays where it is.