/** * User-subject resolution for permission checks. * * Every principal — user, agent, sub-agent — is addressed as a **SubjectSet** * `:` (e.g. `User:`). This is the form the Ory Console's * *Add relationship* dialog writes, the form the local-stack seed writes, and the * Zanzibar-idiomatic composition point (grant `Role:developer` `use` on a tool and * add principals to the role, rather than fanning out a tuple per subject). It is * the only first-class subject form: Keto does no cross-form expansion, so a check * must match the write shape exactly, and standardizing on one shape removes that * silent-misfire footgun. * * The namespace is resolved by precedence: `ORY_USER_SUBJECT_NAMESPACE` (env) → * the client's configured `userSubjectNamespace` (persisted in the shared config) * → the {@link DEFAULT_USER_SUBJECT_NAMESPACE} (`User`). The env/config values now * *override* the default namespace; they are no longer an opt-in switch that * toggles between SubjectSet and a legacy direct SubjectID. */ import type { OryAgentClient } from "./client.js"; /** * Default namespace a resolved user principal is addressed under. The * local-stack seed, the Console *Add relationship* dialog, and the Network OPL * provisioning all use `User`, so it is the out-of-the-box default. */ export declare const DEFAULT_USER_SUBJECT_NAMESPACE = "User"; /** Namespace the agent principal is addressed under (delegation subjects). */ export declare const AGENT_NAMESPACE = "Agent"; /** Namespace a sub-agent principal is addressed under. */ export declare const SUBAGENT_NAMESPACE = "SubAgent"; /** Namespace the no-user-identity `session:` fallback is addressed under. */ export declare const SESSION_NAMESPACE = "Session"; export type UserSubjectRef = { subjectId: string; } | { subjectSet: { namespace: string; object: string; relation: string; }; }; /** * Run `fn` with a per-call user subject that takes precedence over the * client's user principal and the env overrides in * {@link resolveUserSubject}. The override only applies within `fn`'s * async context, so concurrent calls cannot observe each other's subject. * SubjectSet shaping still applies. * * A missing/empty `subject` is a no-op: `fn` runs with the normal * resolution chain. */ export declare function runWithUserSubject(subject: string | undefined, fn: () => T): T; /** * Resolve the user subject for permission checks. Prefers a per-call * override installed via {@link runWithUserSubject}, then the user * login's `userPrincipal.subject`, falls back to `ORY_USER_SUBJECT_ID`, then the * legacy `ORY_AGENT_SUBJECT_ID`, then the caller-supplied `fallback` * (typically `session:`). * * Always returns a SubjectSet. A resolved principal is addressed under the * configured user namespace (`ORY_USER_SUBJECT_NAMESPACE` → client config → * {@link DEFAULT_USER_SUBJECT_NAMESPACE}). A `session:`-prefixed fallback is * addressed under {@link SESSION_NAMESPACE} (`Session:`). Only when nothing * resolves at all do we emit the direct `agent:unknown` sentinel — an error * marker that must never match a stored tuple. */ export declare function resolveUserSubject(client: OryAgentClient, fallback?: string): UserSubjectRef; /** * Printable label for a `UserSubjectRef`. Used in denial messages and * activity attributes. SubjectSets render as `:`. */ export declare function subjectLabel(ref: UserSubjectRef): string; /** Separator between the credential client id and narrower scoping axes. */ export declare const SUBJECT_AXIS_SEPARATOR = "|"; /** The env var that overrides the agent's subject. */ export declare const AGENT_SUBJECT_ID_ENV_VAR = "ORY_AGENT_SUBJECT_ID"; /** * Validate a subject override (issue #241). * * `deriveSubject` above already refuses to produce a trailing separator, * because a subject ending in `|` silently never matches a stored relation. * The same care is owed to the *base* it joins onto, which arrives from an * operator-settable env var and was previously used verbatim. * * A value containing the axis separator is rejected for two independent * reasons: * * 1. **It collapses the identity grains.** The credential, session, and spawn * subjects are told apart only by separator position, so an override * containing one makes them ambiguous: `override="a|b"` in session `c` and * `override="a"` in session `b|c` produce the same string. A session-scoped * block written to stop one runaway run could then match the base credential, or a * different run — and all three grains are read together in one batched * check, so this is a live semantics break rather than a theoretical one. * 2. **It forges a delegation-node reference** (#225). A join key is exactly * `|||`, and the two string spaces stay * disjoint today only because a Hydra client id is a UUID and contains no * separator. This env var is the one supported knob that breaks that. * * Rejected rather than sanitized: silently stripping the separator would * produce a *different* subject than the operator asked for, which fails just * as confusingly and is harder to notice. Ignoring the value falls back to the * client id — a real, working identity — and says so. */ export declare function validateSubjectOverride(raw: string | undefined, envVar?: string): { value?: string; warning?: string; }; /** * Read and validate `ORY_AGENT_SUBJECT_ID`. Returns `undefined` when unset or * rejected, pushing an explanation onto `warnings` in the latter case. * * One reader for every site that consumes the override, so the rule cannot hold * in the resolver and not in what `status` reports. */ export declare function readAgentSubjectOverride(env?: NodeJS.ProcessEnv, warnings?: string[]): string | undefined; /** * The registered agent credential subject: `Agent:`. For DCR this * client belongs to one session; static credentials may be shared explicitly. * * `undefined` when no agent principal is populated — the agent gate never * blocks, so a session with no resolved machine identity is normal and callers * must treat a missing agent subject as "nothing to check", not as a deny. */ export declare function resolveAgentSubject(client: OryAgentClient): UserSubjectRef | undefined; /** * The **session-level** agent subject: `Agent:|`, unique to * one run. Used for a block that stops the session's narrower subject without * blocking its base credential subject. * * `undefined` when there is no agent principal, and — deliberately — also when * there is no session. A caller with no session concept (an SDK integration in * a long-running service) has exactly one sessionless credential subject; * inventing a placeholder session would either collapse every run onto * one string or produce a subject no admin could have written. */ export declare function resolveAgentSessionSubject(client: OryAgentClient, sessionId?: string): UserSubjectRef | undefined; /** * The registered sub-agent credential subject: `SubAgent:` for one * typed sub-agent in one session. * * `subAgentClientId` is the client id from {@link ensureSubAgentIdentity}; * `undefined` in, and `undefined` out, since a sub-agent whose identity did not * resolve has nothing to check. */ export declare function resolveSubAgentSubject(subAgentClientId: string | undefined): UserSubjectRef | undefined; /** * The **spawn-level** sub-agent subject: * `SubAgent:||[|]`, the finest grain available. * * `perSpawnId` distinguishes two *concurrent* same-type sub-agents and is only * present on harnesses that expose one (Cursor's `subagent_id`, Claude Code's * `agent_id`, OpenClaw's `childRunId`, …). Where the harness exposes none the * subject stops at the type and concurrent spawns share it — a limit of the * harness, not of this model. * * As with the agent session subject, a missing session yields `undefined` * rather than a placeholder. */ export declare function resolveSubAgentSpawnSubject(client: OryAgentClient, args: { subAgentClientId?: string; subAgentType: string; perSpawnId?: string; sessionId?: string; }): UserSubjectRef | undefined;