Who can use your app

Your app has no sign-in code in it. Spryloom decides who is at the door and tells the app who arrived.

That is the whole arrangement, and everything below follows from it.


Choosing who

One line in spryloom.yaml:

access:
  visibility: company
  domain: yourcompany.com
visibility Who can open it
private Only you.
invited People you invited by name, and nobody else.
company Anybody with an email address at domain.
link Anybody who has the address, once they have signed in as somebody.

company needs domain. The others do not.

Change it and publish again; it takes effect immediately.

Inviting people

spry invite expense-notes --email ryan@yourcompany.com --email sam@yourcompany.com

They get an email naming the app, what it does, and who invited them. Somebody who cannot use the app is not invited and is not emailed, and you are told:

Invited 1 person.
They'll get an email with a link, and sign in with that.

Not invited:
  someone@gmail.com — not at yourcompany.com

Inviting somebody who already has access is not an error. It is what you asked for, and it is already true.

How people get in

They open the app's address and are asked for their work email. Spryloom sends a link. They click it and they are in, on that app.

No passwords. Nothing for you to build, reset, or store.

A session is for one app. Signing in to one grants nothing on another, even for the same person: the two are separate sessions on separate addresses. That is deliberate. An app somebody wrote in an afternoon should not be able to reach anything else your company runs.

Google sign-in is not available yet. signin: [google] is accepted in a manifest and does nothing, so do not declare it — the app's own page tells your coworkers how they will actually sign in, and declaring a method that is not there would make that page wrong.


How your app knows who is using it

Two headers, on every request. That is all.

const email = request.headers['x-spryloom-email'];  // who they are
const role  = request.headers['x-spryloom-role'];   // 'admin' or 'user'

Trust them. Spryloom deletes every x-spryloom-* header that arrives with a request before it decides anything, so the only way one reaches your app is from Spryloom. A client that sends one gets its request refused outright.

This matters more than it looks. Anything a browser sends — a form field, a query string, a cookie you set — can be changed by whoever is using it. The header cannot. So when your app records who did something, use the header:

// Right: Spryloom said who this is.
await save({ author: request.headers['x-spryloom-email'], body });

// Wrong: the form said who this is, and a form can say anything.
await save({ author: form.get('author'), body });

A third header, x-spryloom-identity, carries the same facts signed, for an app that would rather check them itself. Most apps should not bother.

The two roles

admin is you — whoever published the app — and anybody listed in access.admins. Everybody else is user.

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

Spryloom does not decide what the difference means. Use it where it matters in your app — who can delete things, who sees everything — and ignore it where it does not.

Roles come from the manifest. To make somebody an admin, add them and publish again.


What your coworkers see before they open it

Every app serves /~label — a page Spryloom writes, not your app. It says what the app stores, what it can reach, who can use it, who made it, and when it last changed.

It is generated from what was actually built, not from what the manifest asked for. An app that declared a database and did not get one says it stores nothing.

It is there because somebody is about to open a thing a colleague generated with an AI, and "Priya says it's fine" is not enough to go on.


What Spryloom records

Every sign-in, every refused sign-in, every invitation, every publish, and every rollback, with who did it and when. You can read it in the dashboard.

Nothing in that log is ever a token, a session, or a secret — the log refuses to store one, rather than storing it and hoping.