Imperal Docs
Core Concepts

Federal invariants

Federal invariants — the runtime contracts Imperal Cloud enforces at the boundary so every extension stays safe, audited, and impossible to bypass at runtime.

When we say "federal-grade", we mean a specific thing: guarantees enforced at runtime — not promises, not best practices, not unit tests. Extensions that violate them fail validators at publish time or fail-closed at runtime. This page covers the guarantees extension developers most often rely on. The platform maintains a much larger inventory of internal contracts that govern how the kernel itself is built — those are not something an extension author needs to know.

What a federal guarantee is

A federal guarantee is a written-down property the platform commits to keeping true for every extension — automatically, whether or not you do anything. You don't wire them; you benefit from them. The sections below describe the ones you'll actually notice while building.

The big surfaces

🆔

Identity & tenancy

ctx.user.[imperal_id](/en/reference/glossary/) is canonical, tenant scope is checked at the boundary, and defaults fail closed.

🚫

Anti-hallucination

Chat is protected from fabricated IDs, ungrounded data claims, out-of-enum hints, and narrator factual claims without backing.

Authorization

The acting user is authenticated from a required header, and the confirmation lifecycle gates every write/destructive action.

📝

Audit chokepoint

Every action that touches user data lands in the federal [action ledger](/en/reference/glossary/).

🧬

[Pydantic feedback loop](/en/reference/glossary/)

Bounded retry on validation errors with structured prose feedback.

🌀

Skeleton lifecycle

Skeletons keep a continuous 'life' — rotation, watchdog re-spawn, and live invalidation are all platform-managed.

📡

Context channels

How skeleton, [fact-ledger](/en/concepts/fact-ledger/), and ctx.cache feed the [intent classifier](/en/reference/glossary/). Bounded sizes, staleness envelopes, PII gates.

🔗

Chain orchestration

Multi-step planning, typed pipe between steps, prior outputs, scope-drift validation.

Magic UX

Conversational chat errors — no leaked stack traces, no Pydantic class names visible to users.

🛡️

Confirmation flow

[Pre-Authorized Action Execution](/en/reference/glossary/) — what the user sees IS what fires.

The guarantees you'll notice most

These come up constantly while building. Worth knowing.

No fabricated IDs

If the agent hallucinates an ID it never actually saw — say note_id="abc123" in a delete_note call — the platform catches it and refuses. This holds on every retry too, so a misbehaving model can't fabricate an ID on the second try and slip through.

Confirmations execute exactly what you saw

When a user accepts a confirmation card, the action that runs is byte-identical to the one shown on the card. No model rerun, no argument drift. This is the cornerstone of Pre-Authorized Action Execution.

Bounded validation retry

When typed args fail validation, the agent gets at most 2 retries per tool call with structured feedback. Beyond that, the call fails with a standard VALIDATION_MISSING_FIELD. Nothing loops forever.

Acting-user is authenticated, not spoofable

Accepting or cancelling a confirmation requires an authenticated acting user — there is no body or query fallback to spoof acceptance for another user, and the confirmation endpoints take no action arguments from the request. This closes a class of confirmation-time injection and impersonation attacks.

Placeholder args are rejected before dispatch

When a user's message omits a required field, the agent sometimes substitutes a placeholder token (<UNKNOWN>, <TODO>, <EMAIL>, etc.) instead of asking. The platform rejects these before the action dispatches — so no billing tokens are spent, no misleading audit row is written, and the agent is steered to ask the user a clarifying question instead. (Genuine prose containing angle brackets, and lowercase HTML/XML tags, are not affected.) Landed in SDK v4.2.15.

Manifest validators block publishing

The federal validators run at publish time and most ERROR-severity rules block the package from reaching the marketplace. The ones you'll meet most:

IDChecksSeverity
V14Extension description too short or equal to app_idERROR
V15display_name empty, short, or equal to app_idERROR
V16@chat.function description shorter than 20 charsERROR
V17Pydantic params model is module-scope (not function-local)ERROR
V18@chat.function declares a typed return (-> ActionResult / Pydantic model)ERROR
V19actions_explicit=True; write/destructive tools are chain_callable=TrueERROR
V20write/destructive @chat.function declares effectsWARN
V21icon.svg declared + valid (XML-validated, viewBox required, ≤100KB, no embedded raster)ERROR
V22Lifecycle-hook signatures match the SDK contractERROR
V24Handlers don't access ctx.skeleton.* outside @ext.skeleton (ERROR); write/destructive data_model recommendation (WARN)ERROR / WARN
V31Extension(system=True) reserved for first-party Imperal authorsERROR

Skeletons stay alive

Your skeleton has a continuous "life" the user sees — rotation and watchdog re-spawn are platform-managed. You don't wire any of it.

Confirmation toggles apply uniformly

A user who disables 2-step confirmations in their settings sees uniform behaviour across single-action writes and multi-step chains — the gate consults the master toggle and the per-action-type granularity on both paths.

How they get enforced

📋

At publish time

The federal validators run on every package. Failing any ERROR-severity rule blocks publishing — your extension never reaches the marketplace until fixed.

At runtime

Pre-flight checks on every chat turn (unknown tool, fabricated ID, scope drift, placeholder args). Fail-closed.

📊

Via observability

The platform's observability pipeline tracks violation counts and alerts the platform team automatically.

🔍

At platform review

Changes to the platform that would weaken a guarantee or remove its enforcement are blocked before they ship.

What this means for you

Most invariants are free

If you write extensions the recommended way (@chat.function with Pydantic, ctx.http for HTTP, key state by ctx.user.imperal_id), you satisfy almost everything automatically.

A few are about your discipline

  • Multi-tenant safety — federal can't enforce that you scope state by user. You must.
  • Description quality — V19 catches empty/boilerplate, but a misleading description still confuses the LLM.
  • action_type accuracy — calling something "read" when it sends an email gets caught by audit, but breaks user trust before that.

Don't bypass — design around

If you find yourself wanting to bypass an invariant, either:

  1. The invariant has a real subset that fits your case → use the documented pattern
  2. The invariant doesn't fit your case → file a Dev Portal issue with the use case

Direct bypass attempts get caught at validate time.

Looking up an error message

When your code hits a runtime error, this table maps the symptom to what happened and where to read more. Errors are usually accompanied by an explanatory message — read the message first, then this page if it's still opaque.

Error or symptomWhat it meansWhere to read
SkeletonAccessForbidden raised in handlerctx.skeleton.get() was called outside an @ext.skeleton handler; skeletons are classifier-onlySkeletons
Skeleton section never appears in classifier responsesThe section returned a list of more than 5 items and the platform collapsed it to a count before injectionSkeletons
Skeleton numbers are stale despite recent activityThe classifier was told the data is cached and to fetch fresh for specific metricsSkeletons
Tool call rejected with placeholder argumentThe agent emitted <UNKNOWN> / <TODO> / <EMAIL> as a value; the platform rejected the call before dispatchthis page (above)
Tool call rejected with fabricated idThe agent emitted an id-shaped value that was never in conversation historythis page (above)
PydanticValidationError after retries exhaustedValidation failed 2+ times; standard VALIDATION_MISSING_FIELD returnedPydantic feedback loop
Confirmation accept rejected with acting-user errorAn authenticated acting user is required to accept a confirmation; body fallback is rejectedConfirmations
Confirmation accept executes different args than displayedImpossible by design — the platform always dispatches byte-identical to what the user saw; a mismatch is a platform regression to reportConfirmations
Chain step receives <unresolved $REF> valueA write step depended on a prior step's output that did not resolve; hard-fail by designChain dispatch
Narrator output mentions a number not in the tool resultAnti-fabrication regression; report it to the platform team
imperal build fails with a V## errorA static manifest check failed; the error text identifies which ruleValidators reference

Where to read more

Public pages covering the guarantees extension developers rely on most:

DocSurface
SkeletonsSkeleton lifecycle, compression, staleness behavior
Fact-ledgerFact-ledger cap and PII behavior
Context channelsHow the three classifier-visible channels coordinate
Chain dispatchChain orchestration and placeholder-arg rejection
Pydantic feedback loopBounded validation retry
Confirmations guideConfirmation flow + Pre-Authorized Action Execution
Validators referenceThe static manifest validators

The platform enforces many more contracts that govern how the kernel itself is built (workflow management, audit chokepoint, billing, observability, deploy gates). Those are internal — an extension developer never needs to consult them. If a runtime error message is not covered above, its text will explain what triggered.

What's next

On this page