Runner sharing¶
A personal runner belongs to the member who registered it. By default only that member and account admins can route flows to it or start remote sessions on it. Runner sharing lets the owner, or an admin with the owner's consent, open a runner to a team or to named users in the same account, within limits.
Runner sharing is part of Preloop Cloud and Preloop Enterprise.
What a share covers¶
Each share names:
- Who: one team or one user in the runner's account.
- Scope:
flows,sessions, or both.flowslets the grantee pin the runner in their flows and see it in harness options.sessionslets the grantee start and drive remote sessions on it. - Harnesses (optional): the harness ids the share covers, for example
claude_code. Omit to cover every harness the runner offers. - Session approval:
per_request(default) asks the owner before each session;nonestarts without asking. Flow leases keep the runner's separate flow-consent setting. - Directories (optional): the authorized directory ids the share covers. Omit to cover every authorized directory and tracker checkouts. A list covers only those directories and no tracker checkouts.
A grantee:
- sees only the harnesses and directories the share covers in the session options;
- sees and drives only the sessions they started, never the owner's or another grantee's;
- cannot open a directory with uncommitted changes (owner only).
A share never widens what the runner itself allows: the harness must be enabled for sessions on the host and the directory must be authorized there.
Owner consent¶
When the owner shares their own runner, the share is active at once.
When an admin shares a member's runner, the owner must accept it first. The share shows as waiting until the owner accepts or declines it.
Account admins can change this:
- Account-wide:
PUT /api/v1/account/runner-sharing-policywith{"owner_consent": "required"}(the default) or"not_required". - Per runner:
PUT /api/v1/runners/{runner_id}/sharing-policywith"inherit"(follow the account),"required"or"not_required".
When owner consent is not required for a runner:
- an admin's new share is active at once and is recorded as an admin override;
- remote session starts by anyone other than the owner skip the per-start owner approval.
Changing the policy does not grant access by itself. Shares that were waiting
keep waiting; scopes, harness and directory limits, account isolation and
audit stay in force. GET /api/v1/runners/{runner_id}/shares returns the
effective policy and where it comes from (runner, account or default).
With per_request, a grantee's start creates an owner-only request in
Audit > Approvals, showing requester, harness and directory. The owner
can approve or decline there or through existing approval channels, including
mobile push. The host receives a notice too. The session stays unstarted until
approval; decline or expiry refuses it. The timeout follows the account's
existing approval window. Retrying the same request cannot start two sessions.
Revoking access while approval is pending prevents it from starting even if
the owner later approves.
With none, only requests covered by that active share skip per-start
approval. A narrow none share cannot waive approval for a broader share.
Admin policy overrides skip per-start approval for both modes, while preserving
all access checks.
Sharing from the console¶
On Runners, choose Share runner, select the runner, then a team or user. Choose Sessions, Flows or both, optionally enter harness ids and authorized directory ids, and choose the session approval mode. Empty harness and directory fields cover all allowed choices. Share saves the grant. The same dialog lists current shares and lets the owner accept or decline an admin's pending share, or Revoke a share after confirmation. Admins can change the runner setting or account-wide owner-consent policy in this dialog; the effective setting and its source are visible to members.
The grantee picker loads at most 500 teams and 500 active users; the runner
owner is excluded from the displayed user choices. Larger accounts may have
teams or users that do not appear in the picker. For an omitted grantee, ask
an account admin for the team or user ID and create the share through
POST /api/v1/runners/{runner_id}/shares as shown in the API example below.
The same owner-consent and scope checks apply to API-created shares.
Shared runners appear under Shared with me in the New session picker and are labelled with their owner in the flow runner filter. Reopening the picker loads current access, so a revoked share disappears. The session dialog shows when it is waiting for the owner; closing it stops the console from retrying the start. It does not withdraw the owner's pending approval.
The owner's console bell reports new shares and grantee starts. The host receives share and approval-request notices on its next heartbeat and session start notices through its existing session notifier. The console recovers the latest 50 notices from the last 24 hours after refresh. Host notices deduplicate within the running process and may replay after the runner restarts. Older hosts continue receiving session-start notices; share/request notices require the updated CLI. The audit trail retains the events beyond the notice window.
On an open-source server without the sharing capability the dialog is hidden. A server with the earlier sharing API keeps its per-start approval behavior and hides the approval mode selector until the backend supports it.
Revoking¶
The owner or an admin revokes a share with
DELETE /api/v1/runners/{runner_id}/shares/{share_id}.
- New starts by the grantee are refused at once.
- Running sessions the grantee started are stopped within one runner heartbeat. Every heartbeat re-checks the sessions on the runner that were started by someone other than the owner.
- A pinned flow run authored through the share is no longer leased to the runner.
Audit¶
Each of these writes an audit row with the acting user and the runner owner:
| Action | When |
|---|---|
runner_share.created |
a share is made (scope, limits, consent state) |
runner_share.consent_accepted / runner_share.consent_declined |
the owner answers an admin's share |
runner_share.revoked |
a share is revoked |
runner_share.used |
a flow is pinned to, or a pinned run is leased to, a runner through a share |
runner_session.start_requested |
a session start, with access: share and the share id |
runner_session.consent |
the per-start owner approval, or decision: waived under the override |
runner.owner_notice |
owner-addressed share, approval-request or session-start notification |
runner_sharing.policy_changed |
the account or a runner policy changes (before and after) |
API¶
| Method | Path | Who |
|---|---|---|
| GET | /api/v1/runners/{runner_id}/shares |
owner and admins see every share; other members see the shares that name them |
| POST | /api/v1/runners/{runner_id}/shares |
owner, or admin |
| POST | /api/v1/runners/{runner_id}/shares/{share_id}/accept |
owner |
| POST | /api/v1/runners/{runner_id}/shares/{share_id}/decline |
owner |
| DELETE | /api/v1/runners/{runner_id}/shares/{share_id} |
owner, or admin |
| PUT | /api/v1/runners/{runner_id}/sharing-policy |
admin |
| GET, PUT | /api/v1/account/runner-sharing-policy |
read: members; write: admin |
Example: share a runner with a team for sessions on Claude Code in one directory.
curl -X POST "$PRELOOP_URL/api/v1/runners/$RUNNER_ID/shares" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"grantee_type": "team", "grantee_id": "'$TEAM_ID'",
"scopes": ["sessions"], "harnesses": ["claude_code"],
"directory_ids": ["'$DIRECTORY_ID'"], "approval": "per_request"}'
The console shows the shares and the effective policy on the Runners page.