One logical executor deployment belongs to one owner organization, currently Soter Labs. Several web services/frontends and a worker can share that deployment. Separate environments require separate backing resources. BA Labs, Soter Labs and other Auth0 organizations remain login/affiliation contexts; they are not the owner of every process their members create. No accounts need to move organizations.
Main, standalone backend and worker use createDeploymentApplicationHost. Configure exactly one of
PROCESS_ACCESS_CONFIG (private JSON file path) or PROCESS_ACCESS_CONFIG_JSON (inline JSON). Missing,
invalid or conflicting configuration fails closed. There is no legacy authorization fallback.
PROCESS_BACKEND_CONFIG.applicationAccess is retired and rejected. Backend template/assets/effect
provisioning is still separate from authorization.
A synthetic policy:
{
"deploymentId": "example-production",
"ownerOrganizationId": "example-owner",
"defaultApplicationId": "operator-workflows",
"applications": [
{
"applicationId": "spell-review",
"templateKeys": ["spell-request"],
"documentKeys": ["spell-strategic-overview"],
"access": {
"roles": [{ "key": "reviewer", "permissions": ["templates:read", "processes:read", "processes:write", "spell:review-risk"] }],
"members": [{ "email": "reviewer@example.test", "roles": ["reviewer"] }]
}
},
{
"applicationId": "operator-workflows",
"templateKeys": [],
"access": { "roles": [], "members": [] }
}
],
"operators": [{ "email": "operator@example.test", "permissions": ["templates:read", "database:read"] }]
}
Apps declare roles and email-to-role assignments. The executor authenticates and resolves verified
identities and implements those grants. templateKeys and optional documentKeys are operator-owned
resource bindings, not fields an app user can use to claim someone else's resources. The optional
defaultApplicationId assigns newly authored, otherwise-unassigned template keys to an explicit app;
it never restores old authorization. Without it every key needs an explicit assignment.
Email after trimming is the membership identifier, using exact case consistently with provider lookup. Case-only duplicate declarations are rejected. There is no email-change, account-linking or mailbox inheritance subsystem: change membership explicitly. Role addresses may intentionally survive staff changes. Provider IDs remain internal authentication/audit details. Unverified or blocked identities are not grandfathered in merely because an address appears in the policy.
App grants are confined to their app. They cannot grant database:*, user:impersonate or
templates:write. Publication is deploy-only: templates are code-owned and published at startup
when their compiled digest changes; no user, operator or app role can author runnable definitions over
HTTP. templates:write is retired — a policy that still lists it as an operator permission loads and
the grant is ignored. App template reads remain scoped. Membership and
role edits are reread on requests; changing deployment/app identities or resource ownership requires
a restart and reviewed migration, not an in-place reassignment of live data.
Canonical processes persist {deploymentId, ownerOrganizationId, applicationId} in ownership.
Children inherit the scope. Scoped repositories filter reads/listing and reject foreign writes,
audit and deletes. Template ownership must agree with process ownership. Canonical stores use an
owner namespace; document, file and canonical backing resources also acquire immutable one-owner
claims, preventing accidental reuse by another deployment. These are trusted platform adapters,
not app-author supplied authorization implementations.
Every process command derives permissions for the target app afresh. A combined permission summary
may be used for coarse HTTP admission, never as final target authorization. /api/me returns platform
permissions plus an explicit applicationPermissions map by default. Query processId or templateKey
for target-scoped permissions. Runners use that scope for field/step affordances; list rows carry
effectivePermissions. Browser navigation may use a union only to show available destinations.
The private-notes probe uses process-scoped identity, and document export checks the asset's declared
app membership. Neither can borrow a permission from another app.
GET /api/process walks every application the caller is a member of. An app frontend's server-side
/api proxy narrows that walk: it sets X-Process-Platform-Application from its own deployment
configuration (PROCESS_APPLICATION_ID, validated like PROCESS_AUTH_MOUNT_ID) and always replaces
or removes any browser-supplied value. The backend then runs only that application's branch; an
unknown id is a 404 and a malformed one a 400. Without the header (Main, direct API use) behaviour is
unchanged. This is a scoping default, not a security boundary: membership and permissions still decide
every row, so a non-member of the scoped app gets an empty list, never another app's rows. It is
consistent with "scope is trusted deployment configuration, not a request header": the value comes
from the frontend's deployment configuration, and a caller who sets it directly can only narrow their
own view. Records and exports stay cross-application (their pages offer a template picker).
With templates=shared the list sends each distinct summary template once (templates, keyed by
template key + updatedAt) and rows carry templateRef. The legacy embedded array remains the default
for one release; every in-repo consumer requests the shared shape and expands it with
expandProcessList from @processos/contracts/process, which accepts both.
Member discovery resolves declared emails across affiliations. Process sharing can narrow/widen access within declared app membership; it cannot bypass app membership or permit anonymous access. The login organization header must still agree with the verified session. Body emails, unsigned token decoding and frontend identity/mount names never manufacture authority.
Existing Auth0 organizations/accounts are retained for login. Provider role claims no longer grant application or operator authority. Operator impersonation resolves the target's identity and declared app permissions; the real actor remains separately identified. App grants cannot enable impersonation.
Identity resolution is performance-critical: production access tokens carry no email claims, so each
request needs the provider profile. One HTTP request resolves its principal once (guards, actor and
every app branch share it); the /api middleware only verifies the token and never calls the provider. The Management API token is reused until 60 s before expires_in, with
concurrent callers sharing one fetch, and is dropped if the API rejects it. verifiedIdentity keeps a
bounded per-user cache (PROCESS_IDENTITY_CACHE_TTL_MS, default 30000, 0 disables;
PROCESS_IDENTITY_CACHE_MAX_ENTRIES, default 1000) with single-flight lookups. The TTL is the
revocation lag: a blocked user, a lost verification or an email change can keep resolving to the
cached identity for up to that long on each web process. Failures and unverified results are never
cached. Membership edits are unaffected: the policy is reread on every request.
Starting a host never creates users, sends invitations or changes Auth0. The trusted single-app management helper can assess declared members and explicitly invite them using operator-configured onboarding organization/client settings. Invitations assign no global provider roles and do not return bearer invitation URLs. Unknown external outcomes stop for reconciliation, not automatic retry. A membership editing UI and general app-management API remain follow-ups. Actual Auth0 connections, Management API scopes, invitations and login on each deployed mount require live acceptance.
OAuth login now binds signed state to a per-flow, HttpOnly, SameSite=Lax browser cookie. HTTPS cookies are Secure and host-only; only loopback development may use HTTP. Missing/mismatched browser binding is rejected before code exchange. Silent retry retains the flow binding; terminal callbacks clear it.
This is the only model in the candidate release, not a claim that production has been changed.
All web hosts and workers must cut over together; old writers cannot remain an alternate access path.
The CLI requires --ownership /private/ownership-by-template.json, mapping every template key to
its explicit scope. No creator/email/affiliation inference is used. The source mapping/hash remains
in the plan; missing/conflicting assignments block import. Use the owner-aware stores, not legacy
unnamespaced readers, for import/readback. Unowned records are never adopted on first access.
Review actual membership and resource mappings before activation. A preservation-oriented draft may contain surprisingly broad historic control-plane grants; carrying them forward is not approval of them. Duplicate-email unions and unverified accounts need explicit review. Keep policy/export files private and outside source control. Prefer a private file for large policies rather than one oversized environment string.
Uploads are byte-bounded while streaming, before multipart parsing and Blob buffering. New files get server-stamped activity/uploader metadata. Deletion checks activity, process access, current field permissions/editability and cross-activity references. Legacy files without reliable ownership are not guessed: deletion fails closed until reviewed metadata migration. Preserve their bytes and sidecars.
See cutover for backup, import, deployment holds, acceptance and rollback gates. Local tests do not certify live provider behavior or approve the production policy.