ObjectStackObjectStack

Execution Context — Kernel Protocol reference

Execution Context schemas of the ObjectStack Kernel Protocol: AuthGateCode and 2 more — each property with its type, default and a TypeScript example.

Source: packages/spec/src/kernel/execution-context.zod.ts

TypeScript Usage

import { AuthGateCodeSchema, AuthGateScopeSchema, ExecutionContextSchema } from '@objectstack/spec/kernel';
import type { AuthGateCode, AuthGateScope, ExecutionContext } from '@objectstack/spec/kernel';

// Validate data
const result = AuthGateCodeSchema.parse(data);

AuthGateCode

Allowed Values

  • PASSWORD_EXPIRED
  • PASSWORD_RESET_REQUIRED
  • MFA_REQUIRED

AuthGateScope

Allowed Values

  • account
  • credential

ExecutionContext

Properties

PropertyTypeRequiredDescription
userIdstringoptional
actorstringoptional
attributedUserIdstringoptional
emailstringoptional
tenantIdstringoptional
timezonestringoptional
localestringoptional
currencystringoptional
positionsstring[]optional (default: [])
principalKindEnum<'human' | 'agent' | 'service' | 'guest' | 'system'>optional
audienceEnum<'internal' | 'external'>optional
postureEnum<'PLATFORM_ADMIN' | 'TENANT_ADMIN' | 'MEMBER' | 'EXTERNAL'>optionalADR-0095 D2 posture rung — PLATFORM_ADMIN crosses the tenant wall where object posture permits; TENANT_ADMIN sees all rows in the org; MEMBER gets business RLS; EXTERNAL (an external organization member) reaches explicit grants only — shares, sharing rules, and grant RLS — never the OWD baseline.
authGate{ code: Enum<'PASSWORD_EXPIRED' | 'PASSWORD_RESET_REQUIRED' | 'MFA_REQUIRED'>; message: string }optionalADR-0069 authentication-policy gate: present only while the principal is blocked from protected resources until they remediate (expired password, enforced MFA), absent for every healthy session. code is the stable machine code the client branches on, from the closed vocabulary PASSWORD_EXPIRED / PASSWORD_RESET_REQUIRED (an administrator forced a password change) / MFA_REQUIRED, and message is what the blocked user reads; both are required because the transport seam renders them as the 403 body. Each code declares a scope: MFA_REQUIRED holds the account, so an API key whose owner holds it is refused with the same 403; PASSWORD_EXPIRED and PASSWORD_RESET_REQUIRED hold password sign-in alone, and API keys of that owner keep working. AUTHENTICATION, not authorization — it suspends access entirely rather than narrowing it, and nothing in the permission/RLS path reads it, while the allow-listed remediation endpoints stay reachable. Server-constructed only, never client-supplied; a guest/anonymous principal never carries one.
onBehalfOf{ userId: string; principalKind?: Enum<'human' | 'agent' | 'service' | 'guest' | 'system'> }optional
performedBy{ clientId: string }optionalADR-0090 D10 rule 4 dual attribution: the agent that PERFORMED this operation, when the performer is not the principal the write is authorized as. Set only at the /mcp OAuth door, on the same branch that decides principalKind: agent and onBehalfOf; absent everywhere else, and the absence is the record that the principal acted for itself. ATTRIBUTION ONLY — no security middleware reads it, it never becomes the authorization subject, and userId stays the human so owner-stamping and current_user.* RLS still resolve to them. Server-constructed only, never client-supplied. Surfaced to hooks as HookContext.provenance.performedByClientId and recorded by the audit writer as sys_audit_log.metadata.performed_by beside on_behalf_of; it does not move actor, which ADR-0118 D1/D5 keeps two-valued.
surfaceEnum<'rest' | 'mcp'>optionalThe door an action invocation came through: rest (the REST POST /actions/:object/:action door — console buttons, the SDK) or mcp (the /mcp bridge's run_action and resume_run tools — an AI client). Set only by those two doors, on the envelope of the invocation they hand the audit writer, from the door's own identity (an upstream value, header or body member never moves it); absent on every other operation. ATTRIBUTION ONLY — no security middleware reads it and it is never an authorization subject, scope or gate. Server-constructed only, never client-supplied. Recorded by the audit writer as sys_audit_log.metadata.surface on the action_invocation row.
permissionsstring[]optional (default: [])
systemPermissionsstring[]optional
tabPermissionsRecord<string, Enum<'visible' | 'hidden' | 'default_on' | 'default_off'>>optional
org_user_idsstring[]optional
accessible_org_idsstring[]optional
rlsMembershipRecord<string, string[]>optional
isSystembooleanoptional (default: false)
flowRunIdstringoptional
skipTriggersbooleanoptional
skipAutomationsbooleanoptional
seedReplaybooleanoptional
skipStateMachinebooleanoptional
preserveAuditbooleanoptionalHistorical import: preserve the ORIGINAL audit timeline for this write instead of stamping it "now". Opt-in and server-constructed only, never client-supplied. On the UPDATE path it admits a whitelist — the audit/timestamp family (created_at / created_by / updated_at / updated_by) plus author-declared business readonly fields — while platform-managed system columns (tenancy, generated) stay stripped. On INSERT the exemption does NOT apply: the create-side static readonly strip runs inside engine.insert itself (after the beforeInsert hooks, before validation — the 2026-09-03 ruling; the DataProtocol ingress copy it replaced is deleted) and reads only context.isSystem, so a non-system create carrying preserveAudit still has those fields stripped and is warned (WARN) that the exemption is UPDATE-only — replaying archival readonly facts on create requires a system context. Permissions / RLS / field-level security are unaffected.
keptOptionValuesRecord<string, string[]>optionalImport with createMissingOptions: per field name, the option values the import kept because they matched none of the field's options. The write-path option check admits exactly these values on this write (and still refuses every other value outside the options); the field's options are not changed, and a later write that sends the field is judged against them again. Server-constructed only (set by the import runner), never client-supplied.
oauthScopesstring[]optional
accessTokenstringoptional
transactionanyoptional
traceIdstringoptional

On this page