Skip to content

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. flows lets the grantee pin the runner in their flows and see it in harness options. sessions lets 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; none starts 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.

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-policy with {"owner_consent": "required"} (the default) or "not_required".
  • Per runner: PUT /api/v1/runners/{runner_id}/sharing-policy with "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.