🔐

Portal Authorization

The kernel's stance permission layer

The system that determines what one stance can do toward another stance through a portal connection. One function, four inputs, allow-or-deny output. Every verb call from every stance at every position flows through it. It is what makes the protocol's stance commitment real at the kernel level.

What it is

A single kernel function. Every verb call passes through it before the verb's own handler runs. Reads per-stance permissions from land metadata, applies the configuration, returns allow or deny.

authorize(
  actingStance,   // who is making the request
  target,         // a Position or a Stance
  verb,           // "see" | "do" | "summon" | "be"
  details         // { action?, payload?, operation?, message? }
) → "allow" | "deny"

Same function for human stances and AI stances. Same function for within-land requests and cross-land requests. The protocol does not split authorization into separate cases — it splits stances and assigns them permissions per land.

The four inputs

Each verb call carries the four pieces the authorization function needs. They come from different parts of the request.

1
Acting stance
From the identity token

The left side of the portal connection. Who is making the request. The kernel reads it from the identity token established by BE, or treats the requester as the land's arrival stance if no identity is present.

2
Target
From the envelope's address field

The right side of the portal connection. A Position (for SEE, DO) or a Stance (for SEE, SUMMON, BE). What is being addressed.

3
Verb and details
From the envelope

SEE, DO, SUMMON, or BE, plus the verb-specific specifics. The action name and payload namespace for DO. The message and intent for SUMMON. The operation for BE. Different details for different verbs.

4
Receiving land's configuration
From the receiving land's metadata

The permission rules the receiving land has configured for the acting stance. Read from metadata.<stance>.permissions at the Land Position. This is what gives the land sovereignty over access.

What it makes real

Before this layer exists, the protocol's claims about stances and lands are conceptual. With this layer in the kernel, the claims become operational behavior.

Stances become first-class

A stance is no longer just a label. It has enforced permissions. Whatever the land says a stance can do, the kernel makes true. The protocol's stance commitment is structural, not aspirational.

Lands gain real sovereignty

Each land's permission configuration defines its access policy. Different lands can be radically different in openness while running the same protocol. The protocol does not enforce a posture; it enforces whatever the land has set.

Beings are checked too

Beings invoking other beings through SUMMON flow through the same function. The protocol does not split "user requests" from "being requests" at the authorization layer; both are stance-bearing entities making verb calls.

Federation has a substrate

Cross-land authorization is just "the receiving land's authorization function checking the visitor's stance against its configuration." The infrastructure for federation already works; only the question of how stances get assigned across lands remains open.

One shape for every stance

Arrival is the first stance every land has. It is not a special case. The same permission configuration shape works for any stance a land defines.

// at the Land Position
metadata.beings.arrival.permissions   = { see, do, summon, be }
metadata.beings.owner.permissions     = { see, do, summon, be }
metadata.beings.guest.permissions     = { see, do, summon, be }
metadata.beings.member.permissions    = { see, do, summon, be }
metadata.beings.moderator.permissions = { see, do, summon, be }
// ...whatever stance vocabulary the land defines

// verb shape (simple allow-lists per verb):
metadata.beings.<stance>.permissions = {
  see:  { allowed_visibility: ["public"] | [] },
  do:   { allowed_actions:    [] | ["action", ...] | "*" },
  summon: { allowed_targets:    [] | ["@being", ...] | "*" },
  be:   { allowed_operations: ["register", "claim", "release", "switch"] }
}

Same per-verb shape across every stance. Same authorize function reads each. Land owners shape the stance taxonomy that fits their land's character. The protocol stays uniform underneath.

For the arrival stance's permissions in concrete detail: see the arrival stance page.

Two cases the auth function handles

The acting-stance input to the auth function comes from one of two places, and that distinction is what arrival is actually for.

No identity token on the request

The requester is a stranger to the protocol — no signed-in identity anywhere. The land treats them as arrival stance and looks up metadata.beings.arrival.permissions on its own configuration. This is what arrival is for: visitors with no identity at all.

Identity token present on the request

The requester is identified somewhere. The receiving land reads the identity and looks up its own policy for that identity. The visitor stays themselves. They don't get reset to arrival just because they crossed a land boundary. The land's policy decides what stance they hold here (member, contributor, default-guest, or whatever the land has configured).

Same function in both cases. Same configuration shape. The only thing that differs is which stance the function looks up.

Authorization is local. Federation is separate.

Two distinct design problems. The auth function answers "given this stance at this land, what's permitted?" Federation answers "given this identified visitor coming from elsewhere, what stance should this land assign?" These are different layers, and the protocol keeps them separate so each can evolve on its own terms.

Within-land authorization. Each land authorizes its own people fully. Owner has full permissions at their land. Arrivals have whatever the arrival stance is configured for. Identified members of a land have the stance that land granted them. The kernel function works end-to-end for any request that arrives carrying an identity the land recognizes, or no identity at all (which routes to arrival).

Cross-land authorization. When an identity carrying provenance from another land reaches this one, the receiving land needs a policy for how to recognize that identity and what stance to assign. This is identity portability, trust roots between lands, and stance-assignment policy. Those are real design problems and they don't change the auth function. They feed it.

The auth layer asks: given this stance, what's permitted? It does not ask how the stance was assigned. That separation is what keeps federation work from forcing rewrites of the kernel authorization layer.

Why this is load-bearing

Every verb call in the protocol flows through this function. Every visit to a position. Every mutation. Every message delivered to an inbox. Every identity operation. There is no path around it. It is the single point where the protocol's access policy is enforced.

This means three things matter especially. The semantics of the permission configuration language must be specified before any code, because every land writes against them. The function itself needs to be fast and well-tested, because it is on every request's hot path. The configurations land owners write are security policy, so tooling for "what can this stance actually do here" matters as much as the function itself.

Different lands feel different because they configure differently. Portal Authorization is the layer that makes "configure differently" actually mean different behavior, not just different documentation.