Share an app
Sharing gives one email address a role on one app. You can also share an app with everyone at a domain, or with a group of people the workspace keeps. The edge checks who the app is shared with on every request, and lets in only them.
Share with one person
Section titled “Share with one person”forculus share acme/hello sam@example.comsam@example.com can now use acme/hello as a viewer. We emailed them the app's link.Sam gets an email with the app’s link, unless the day’s invitation limit is reached. They open it, sign in with a code sent to that address (or with Google or Microsoft), and the app loads. They don’t need a password or an account first.
A viewer doesn’t have to be a member of your workspace. They reach the app by its link, and can’t see anything else in the workspace. To find it again, they can sign in to the dashboard and look under Shared with you, or run:
forculus app list --sharedacme/hello viewer https://hello--acme.forculuslabs-apps.tech shared by you@example.comPick a role
Section titled “Pick a role”--role sets what they can do. The default is viewer.
| Role | What they can do |
|---|---|
viewer |
Use the app. |
editor |
Also deploy it, and manage its configuration, secrets and tokens. |
owner |
Also share it, and delete it. |
Your app gets the caller’s role on every request, in the identity token. The SDKs read it for you.
Editors and owners have to be members of the workspace first:
forculus share acme/hello dana@example.com --role editorerror: Editors and owners must be members of the workspace. Add them first with `forculus workspace add acme dana@example.com`, then share again.A workspace owner or admin adds them with forculus workspace add, as Members and invitations shows, then shares again. If you own the app but aren’t a workspace owner or admin, you’re told to ask one of them. In the dashboard, Share does both steps at once for a workspace owner or admin. See In the dashboard.
To change someone’s role, share with them again and give the new one. A role change sends no email.
Share with everyone at your company’s domain
Section titled “Share with everyone at your company’s domain”forculus share acme/hello @example.comEveryone whose address is at @example.com can now use acme/hello as a viewer.This is how you keep an app to people with an address at your company, without listing each of them. Anyone who signs in with an address at example.com can now open the app as a viewer. Nobody gets an email, since there’s nobody to send it to. Send them the link yourself.
You can only share with the domain of your own address. Signing in proves you have an address there, and nothing else would show you belong to that company. The share stays when you change address or leave, until someone who can share the app removes it. If you’re at example.com and try another domain:
forculus share acme/hello @example.orgerror: You can only share with the domain of your own address, example.com. To share with example.org, ask someone whose address is at example.org.A domain matches exactly. @example.com lets in sam@example.com, but not sam@mail.example.com or sam@example.com.au. A subdomain is a domain of its own, so only someone whose address is at mail.example.com can share with it. Upper case is fine: the domain and each address are compared in lower case.
A domain is only ever a viewer. Editors and owners must be members of the workspace, so share with each of them by email:
forculus share acme/hello @example.com --role editorerror: A domain can only be a viewer. Editors and owners must be members of the workspace, so share with each of them by email.Some domains are refused, because anyone can get an address there:
- A mail provider anyone can sign up with, like
gmail.com,outlook.comorproton.me, under any country’s suffix for the biggest ones, solive.frandzoho.euare refused too:Anyone can sign up for an address at gmail.com, so sharing with it would share with everyone. Share with people by address instead. - A mail testing service’s domain, like
resend.dev, whose addresses take a test send and reach nobody’s inbox:resend.dev is a mail testing service's domain, for test sends that reach nobody's inbox, so sharing with it would share with every test account. Share with people by address instead. - A public suffix, like
co.ukorgithub.io:co.uk is a public suffix, so anyone can register a domain under it. Give your organization's own domain, like acme.com. - A name under no suffix we know, like
acme.internal:acme.internal isn't on a domain anyone can register, so it can't be your organization's. Give your organization's own domain, like acme.com. - A name with a character outside plain ASCII, which could pass for another domain. Give its
xn--form instead. - A wildcard or a leading dot, like
*.example.com. Give the domain itself.
An app can be shared with up to 20 domains. When the workspace has allowed email domains, you can only share with one of them.
Share with a group
Section titled “Share with a group”A group is a named list of people the workspace keeps, like finance or contractors. Share an app with it, and everyone in it can open the app as a viewer:
forculus share acme/hello --group financeEveryone in the finance group can now use acme/hello as a viewer.Who is in the group decides who gets in. Add someone to it, and within a minute they can open every app shared with it. Take them out, and within a minute they can’t. Like a domain, a group is only ever a viewer, and nobody is emailed. An app can be shared with up to 50 groups.
Workspace owners and admins make groups and choose who is in them. See Groups. App owners can share their apps with any group in the workspace.
See who has access
Section titled “See who has access”forculus access acme/helloowner ana@example.comeditor dana@example.comviewer lee@example.comviewer sam@example.comviewer everyone at @example.comviewer group financeThe list shows everyone the app is shared with, owners first, then editors, then viewers, each by email. Its domains and groups come after them. Whoever created the app is its first owner. Only people who can share the app see its domains and groups. When the app is open to the whole workspace, the list ends with that:
viewer everyone in acmeforculus --json access acme/hello gives one object with three lists, grants for people, domains and groups, and says who shared the app with each and when. visibility is workspace when every member can open it, and private when only the people and the domains and groups listed can:
{ "grants": [ { "email": "ana@example.com", "role": "owner", "granted_by": "ana@example.com", "created_at": "2026-10-01T09:12:44Z" }, { "email": "sam@example.com", "role": "viewer", "granted_by": "ana@example.com", "created_at": "2026-10-02T14:05:31Z" } ], "domains": [ { "domain": "example.com", "role": "viewer", "granted_by": "ana@example.com", "created_at": "2026-10-02T14:06:10Z" } ], "groups": [ { "group": "finance", "role": "viewer", "granted_by": "ana@example.com", "created_at": "2026-10-02T14:07:02Z" } ], "visibility": "private"}domains and groups are empty for someone who can’t share the app. With --json, the count of people waiting for access is left out: forculus --json access requests acme/hello lists them.
Someone can get in more than one way, like by their own share and by their domain. They get the highest role any of them gives.
If you can share the app and people are waiting for access, the list ends with how many:
2 people are waiting for access. `forculus access requests acme/hello` shows who.Open it to the whole workspace
Section titled “Open it to the whole workspace”forculus app visibility acme/hello workspaceEveryone in acme can view acme/hello.Every member of the workspace can now open the app as a viewer. People it’s shared with keep their role.
To close it again:
forculus app visibility acme/hello privateacme/hello is private. Only people you share it with can reach it.New apps start private. To make an app that starts open to the workspace, use forculus deploy --create --visibility workspace or forculus app create --visibility workspace.
Take access away
Section titled “Take access away”forculus unshare acme/hello lee@example.comlee@example.com loses access to acme/hello within 60 seconds.Within a minute, Lee’s requests are refused, and any WebSocket, stream or download Lee has open is closed. Any AI client Lee connected to the app is disconnected too, and stays disconnected if you share the app with Lee again. From then on the app answers Lee like this:
forculus curl acme/hellonot foundforculus curl prints the status, 404 Not Found, on stderr. The app isn’t shared with Lee, so the edge answers as if it didn’t exist. Without a share, nobody can tell which apps a workspace has, not even its members.
An app always keeps at least one owner. Removing the last one, or taking the role from them, is refused:
error: An app needs at least one owner.When you’re the last owner yourself, you hear it as yours. forculus share gives a viewer unless you name a role, so sharing the app with yourself is refused too:
error: You're its only owner, and an app needs at least one. To change your own access, make someone else an owner first.Sharing with someone, you included, in the role they already have changes nothing, and nothing goes in the audit log:
lee@example.com is already a viewer of acme/hello. Nothing changed.If the person had no grant, you see:
error: acme/hello isn't shared with lee@example.com, or you can't see it. `forculus access acme/hello` shows who has it.To unshare a domain or a group, name it as you shared it:
forculus unshare acme/hello @example.comEveryone at @example.com loses access to acme/hello within 60 seconds, unless it's shared with them another way.forculus unshare acme/hello --group financeEveryone in the finance group loses access to acme/hello within 60 seconds, unless it's shared with them another way.Someone the app is also shared with by email, or by another group, keeps that access.
It works the other way too. Unsharing a person ends only the access you gave them by email. If the app is also shared with their domain, or with a group they’re in, they can still open it as a viewer, and you’re told:
lee@example.com loses the access you gave them by email, but acme/hello is still shared with @example.com, which lets them in as a viewer.A group is named as the finance group, and an app open to the whole workspace as everyone in the workspace, when Lee is a member.
The grant.remove entry in the audit log lists what still lets them in under still_shared_by. To shut them out, unshare the domain or take them out of the group.
Removing someone from the workspace also removes every grant they had on its apps, and takes them out of its groups. An app shared with their whole domain still lets them in. See Members and invitations.
Unsharing also ends the bypass tokens Lee made for the app, unless Lee can still reach it: when the app is open to the whole workspace and Lee is still a member, or a domain or a group still lets Lee in. Then they keep working as viewer tokens. The same goes when you unshare a domain or a group, take someone out of a group, or delete one. If you make Lee a viewer, Lee’s editor tokens work as viewer tokens for as long as Lee stays one. See When its maker loses access.
Asking for access
Section titled “Asking for access”Someone who signs in and can’t open an app gets a page saying there’s nothing there they can open, with a link: Ask for access. It opens a form in the dashboard with the app’s address filled in and room for a note of up to 500 characters. They select Send, and see this whatever they asked about:
If this app exists and you can be given access, its owners will get your request.They’re never told whether the app exists, whether their request went anywhere, or what you decided. The page they were turned away with reads the same for an app that’s there and one that isn’t, so asking tells them nothing either.
A request reaches the app’s owners only when there’s a live app at that address, the workspace could share it with them under its allowed email domains, and they can’t open it already. Anything else is dropped without a word. So are requests past these limits:
- One open request per person per app. A request stays open for 30 days.
- Five requests an hour from one person, whatever they ask about.
- Ten a day for one app, and fifty a day for one workspace.
- After you deny a request, the same person’s requests for that app are dropped for a week.
To the person who sent it, a dropped request looks like any other: they see the message above. Nobody else hears of it. No owner gets an email, it isn’t on Sharing, and it isn’t in the audit log.
Decide on a request
Section titled “Decide on a request”The app’s owners and the workspace’s owners and admins get an email saying who asked and what they wrote. Any links in the note are shown broken, so none can be clicked. The email links to the app’s Sharing tab. Nothing in it approves anything.
On Sharing, Asking for access lists the open requests. Approve shares the app with them as a viewer and emails them its link, as a share does. Deny closes the request, and they aren’t told. If they ask again within a week, that request is dropped as above. If you’ve shared the app with them some other way since they asked, the request leaves the list, and approving it keeps the role they have.
From the CLI, list the requests and approve one by its id:
forculus access requests acme/helloforculus access approve acme/hello 7d1f0c2e-4b9a-4c55-9f0e-2a61b3c4d5e6pat@example.com can now use acme/hello as a viewer. We emailed them the app's link.Each request shows its id, who asked and the day they asked, with their note on the next line. forculus access deny takes the same arguments as approve.
A request someone else answered first, often another owner a moment before, is refused with 409 already_decided, and the message says which way it went: That request was already turned down. On Sharing the request leaves the list, and a note says the same.
Each request, approval and denial is in the audit log, as access_request.create, access_request.approve and access_request.deny.
People turned away
Section titled “People turned away”Once a day at most for each app, the workspace’s owners and admins get an email listing who signed in and was turned away from it because it isn’t shared with them: up to ten addresses, with how many times each, and how many more there were. It comes only when someone was turned away. Visits from people who weren’t signed in aren’t in it, and nor are the workspace’s own owners and admins, who can share the app with themselves. Nor is anyone who can open the app by the time the email goes, such as someone whose request was approved after they were turned away.
How fast a change lands
Section titled “How fast a change lands”| Change | When it takes effect |
|---|---|
| Share with a person, a domain or a group, add someone to a group, or open the app to the workspace | Within 60 seconds |
| Unshare, make the app private, remove a member, take someone out of a group, or delete a group | Within 60 seconds, open connections included |
| Revoke a bypass token | On the token’s next request. A connection already open with it closes within 15 seconds |
| Take away the access of someone who made a bypass token | On the token’s next request, as for a revoke. See When its maker loses access |
| Delete the app | Within 60 seconds in a browser and on its public paths, open connections included. Straight away for bypass tokens and forculus curl |
For a browser, the edge caches the answer for under a minute, then asks our control plane again. Many copies of the edge run in each Cloudflare location, and each has its own cache.
Once one copy refuses a person, the others in that location drop what they cached, so every later request from that person there is refused too. Open connections are checked while they run and closed once access ends.
A connection opened with a bypass token or a CLI session is checked every 10 seconds. It closes within 15 seconds of the token being revoked, the session ending or the person losing access.
Who can share
Section titled “Who can share”App owners, and the workspace’s owners and admins, can share an app with people, domains and groups, unshare it and change its visibility. An editor who tries gets:
error: Only hello's owners and the workspace's owners and admins can share it.The same goes for reading and answering its requests for access. Sharing an app that isn’t in the workspace tells its owners and admins so, and where to find the ones it has. They see every app in it, so the list is whole:
error: There's no app nosuch in acme. `forculus app list acme` lists its apps.Anyone else is told only that there’s no such app they can see, since an app they can’t see may be there.
Your workspace’s owners can keep opening an app to everyone, and sharing outside the workspace, to the people they name, with a publish policy. Someone it stops is told so:
error: acme's publish policy doesn't let you share apps with people outside the workspace. A workspace owner can change it.Share only inside your company
Section titled “Share only inside your company”When the workspace has allowed email domains, you can only share with addresses at those domains:
forculus share acme/hello pat@gmail.comerror: acme only allows addresses at example.com, so hello can't be shared with pat@gmail.com.Sharing with a domain that isn’t on the list is refused the same way. The list also shuts out people at other domains the app was already shared with, and a domain shared before the list was set lets nobody in. See Limit who can join.
Invitation emails
Section titled “Invitation emails”Each new share sends one email, with the app’s address as the link. Sharing with the same person again sends nothing.
A workspace sends at most 50 invitation emails in any 24 hours, and one person sends at most 100 across all their workspaces. Forculus also limits how many it sends in a day across all workspaces, which leaves room for sign-in codes. Past any of these limits, the share still works but no email goes out. The dashboard, the CLI and the MCP server tell you when that happens and which limit it was, so you can send the link yourself:
lee@example.com can now use acme/hello as a viewer. We didn't email them: you've sent all the invitation emails one person may in a day. Send them the app's link yourself.If the email couldn’t be sent for another reason, the CLI says The email to them didn't go out. instead.
If nothing is deployed yet, the link won’t open until a version is live.
In the dashboard
Section titled “In the dashboard”Open the app and select Share at the top of the page. App owners and workspace owners and admins see it on every tab. It takes three steps, or four when the person has to join the workspace:
- Email. Type their address. A typo, or an address outside the workspace’s allowed email domains, is caught here, before anything is sent.
- Access. Pick Viewer, Editor or Owner. Each says what it lets them do. If they already have access, you see their role now and can change it. Some roles can’t be picked, and the step says why:
- When your plan has no room for another member, you can’t pick Editor or Owner for someone who isn’t a member yet, because they’d have to join. A viewer doesn’t need to join. The step links to the plan.
- The app’s only owner stays an owner, you included. Make someone else an owner first.
- Workspace. You see this only when you pick editor or owner for someone who isn’t a member. Editors and owners have to be members, so they’re added when you confirm. The step says what a member can do, and how many more members your plan has room for.
- Confirm. Check who, what and whether an email goes out, then select Share. When they’re joining too, the button names the workspace, like Add to Acme and share.
The last screen tells you what happened and what email they’ll get. They open the link and sign in with a code sent to their address. The screen shows the app’s link to copy, in case the email didn’t go out.
If you own the app but aren’t a workspace owner or admin, you can’t add people to the workspace. For someone who isn’t a member, you can’t pick Editor or Owner, and the step tells you which owners and admins to ask. You can still make them a viewer.
The rest of Sharing:
- Who can open it switches between the people the app is shared with and everyone in the workspace. Opening it to the workspace asks you to confirm.
- People lists everyone the app is shared with. Each row has a role menu and a remove button. When a workspace owner or admin makes a viewer who isn’t a member an editor, the menu offers Add to the workspace and make editor.
- Domains and groups lists the domains and groups the app is shared with, each with a remove button. Type a domain and select Share, or pick one of the workspace’s groups and select Share.
- Asking for access lists the open requests, when there are any, with Approve and Deny. See Asking for access.
Next steps
Section titled “Next steps”- Workspaces and roles: what each workspace role can do.
- How access works: the path of one request through the edge.
- Tokens for scripts and machines: access for something that can’t sign in.
- Audit log: every share and unshare is in it, as
grant.putandgrant.remove, and for a domain or a groupgrant.domain_put,grant.domain_remove,grant.group_putandgrant.group_remove.