Skip to content

Initiator and principal

IdentitySDKHTTPexecuteAsexecute_asgetActivityIdentityinitiated_byexecuting_as

A run records who asked and who executes as separate facts, so a service account can do the work while the audit still names the person.

The situation

A nightly report runs with a read-only database credential that no person holds. When someone triggers it by hand, it should still run as the report account, and the audit should show both who pressed the button and which account did the work.

The recipe

Name the service account on the workflow’s profile:

start.ts
workers: [{
taskQueue: 'reports',
workflow: nightlyReport,
config: { invocable: true, executeAs: 'data-bot' },
}],

Read both identities inside any activity:

activities/report.ts
import { getActivityIdentity } from '@hotmeshio/long-tail';
export async function loadOrders(input: { since: string }) {
const identity = getActivityIdentity();
if (!identity) throw new Error('getActivityIdentity runs inside an activity');
identity.principal.id; // data-bot's user id
identity.principal.type; // 'bot'
identity.principal.roles; // the roles data-bot holds
identity.initiatingPrincipal; // the person who started the run
}

A caller can also pass execute_as in the invocation body. A superadmin may act as any account; a caller with an admin-type grant may act as a non-superadmin account whose every role the caller holds at the same or a higher type.

The guarantee

The initiator is stored as initiated_by and never changes, even when execution is delegated. The principal decides RBAC and credential access. Every task row carries initiated_by, principal_type and executing_as, so “every run this account executed for that person” is one query. An execute_as override never exceeds the caller’s own authority; anyone else is refused with 403. See Identity.