---
type: Axis
title: principal (identity axis)
description: The principal/identity architecture axis — who the system can name, authenticate, and attribute actions/state/permissions to (singular → per-principal → RBAC → groups/ABAC). A system-singleton axis whose commitment surface is the persistent-data schema; the principal is the axis-neutral primitive, "user" only the human-tier label (RFC #75 §3.2). DEEP (RFC #75 5A.4 / P1 #851 → promoted 4.5.0a6 #856); worked example is the singular-principal posture where regenerator/migration writes stamp a SYSTEM (not human) principal — the type-correctness the principal reframe exists for.
tags: [axis, architecture, principal, identity, auth, rbac, abac, rfc-75, deep]
timestamp: 2026-06-18T00:00:00Z
---

# Principal (identity axis)

> **DEEP axis (P1 #851 → 4.5.0a6 #856).** Full §6 depth with a worked example
> from the skill's own history — the skill sits at singular-principal, but the
> reframe earns its keep even there: when a regenerator or migration writes
> provenance, the `created_by` it stamps is a SYSTEM principal, not a human, which
> is exactly the 'a process did this, not a person' case the principal primitive
> (not "user") makes type-correct (RFC #75 §3.2/§5). The worked example walks why
> the climb to per-principal has never been forced and what would force it.

## Concept

The principal axis (RFC #75 §3.2) is about **who** the system can name,
authenticate, and attribute actions/state/permissions to. The **principal** is
the axis-neutral primitive — a specialization of the entity (§3.1) that can
authenticate + bear permissions; **"user" is merely the human-tier label.**
Naming the primitive *principal* is what lets the axis span humans, service
accounts, system processes, and devices, plus delegation. A principal carries a
stable id, a **type** (`human | service | system | device`), the credentials
that authenticated it, and the attributes the authz layer reads. The ladder runs
singular (one implicit principal) → per-principal (a named subject) → RBAC (a
principal bearing roles) → groups/ABAC (a principal resolved through
memberships/attributes). Its **commitment surface is the persistent-data
schema** (§4); its **scope is system-singleton** (§5).

## Why it is an axis (discriminator test — §2)

1. **Ordered tiers** — each rung admits a richer notion of the acting subject.
2. **Cross-cutting bundle** — a rung dictates the identity schema, the auth flow,
   the provenance columns (`created_by` holds a *principal*, typed), the authz
   layer, and the tests.
3. **Asymmetric migration cost** — going from singular to per-principal means
   retrofitting an owner onto every existing row (there is no true historical
   owner to backfill); widening the principal *type* breaks `created_by`
   assumptions.
4. **Requirement-keyed selection** — falsifiable probes pick the rung.

## Schema view (the §6 entry — validated by `axis_catalog`)

```yaml
id: principal
kind: architecture-axis
scope: system-singleton
selection:
  probes:
    - "Is there exactly one implicit actor (a single-operator tool)? → singular."
    - "Must the system name + authenticate distinct subjects + attribute state to them? → per-principal."
    - "Are there NON-human actors — service accounts, system jobs, devices (principal-type ≠ human)? → at least per-principal, typed."
    - "Do two principals need DIFFERENT capabilities on the same resource? → RBAC."
    - "Is a principal's authority resolved through group memberships / attributes? → groups/ABAC."
    - "Does one principal ACT ON BEHALF OF another (delegation/impersonation)? → the authenticated ≠ effective principal sub-axis applies (RFC #75 OQ6)."
composes_with:
  - "provenance = principal × temporality (created_by holds a typed principal)"
  - "access control = principal × authz-granularity"
  - "tenant membership = principal × tenancy"
  - "audit log = principal × temporality × authorization"
applies_when: []
tiers:
  - tier: singular
    commitment_delta:
      surface: persistent-data-schema
      delta: "one implicit, often unnamed principal; no owner column, no auth. All state belongs to 'the operator'."
    admin_tool: "none — there is one actor."
    enforcement:
      - "no per-subject attribution is attempted; provenance, if stamped, is a system principal."
    tests:
      - "operations succeed without an identity; a stamped created_by is the system principal."
    forecloses: "attributing state/actions to distinct subjects."
    migration_cost_to_next: "high — add an identity schema + auth + retrofit an owner onto every entity."
  - tier: per-principal
    commitment_delta:
      surface: persistent-data-schema
      delta: "a named, authenticated subject with a stable id + a type (human|service|system|device); state + actions are attributed to it (created_by/updated_by hold a typed principal)."
    admin_tool: "principal directory (create/disable); credential management."
    enforcement:
      - "every attributable write records the acting principal; created_by is typed, never assumed human."
    tests:
      - "a write by a service principal stamps a valid typed created_by; a human + a service are distinguished."
    forecloses: "differential capabilities per principal on the same resource."
    migration_cost_to_next: "medium — introduce roles a principal bears."
  - tier: rbac
    commitment_delta:
      surface: persistent-data-schema
      delta: "a principal bears one or more roles; capabilities are granted to roles, not individuals. Roles span human + service principals."
    admin_tool: "role editor + role-assignment console."
    enforcement:
      - "capability checks resolve through the principal's roles; a role change re-grants without per-principal edits."
    tests:
      - "two principals with different roles get different capabilities on the same resource."
    forecloses: "per-individual-only grants; capability now flows through roles."
    migration_cost_to_next: "medium — resolve authority through memberships/attributes (groups/ABAC)."
  - tier: groups-abac
    commitment_delta:
      surface: persistent-data-schema
      delta: "a principal's authority is resolved through group memberships + attributes (department, clearance, ownership) evaluated against policy."
    admin_tool: "group/membership manager + attribute-policy editor."
    enforcement:
      - "authority is computed from attributes + policy at decision time, not stored per principal."
    tests:
      - "a principal gains/loses capability when an attribute/membership changes, with no per-principal grant edit."
    forecloses: "static per-principal/per-role grants as the only model."
    migration_cost_to_next: "n/a — top of the ladder."
examples:
  - "The skill sits at SINGULAR — one implicit operator per project, no identity schema, no auth — and the worked depth is precisely *why the primitive matters even at the floor*. There is no human-owner column on any canonical entity; the only attribution that exists is provenance stamped by automation, and that is where the reframe pays off: when a regenerator, a migration, or the daemon's derive-on-write path stamps `created_by`/`updated_by`, the principal it records is a SYSTEM principal (type=system), not a human — which is exactly the 'a process did this, not a person' case that naming the primitive *principal* (rather than *user*) makes type-correct. Had the schema modelled `created_by` as a user FK, every automation-written row would be a lie or a NULL; modelling it as a typed principal lets the singular rung be honest. The climb to PER-PRINCIPAL has never been forced because the skill is a single-operator tool (the de-escalation probe answers 'exactly one implicit actor: yes'), and the cost named here is real — going to per-principal means retrofitting an owner onto every existing row with no true historical owner to backfill. The drift signal this axis defines is live and load-bearing: if code ever grew a service principal in provenance or ≥2 roles while the system record still says singular, that is the architecture_drift_gap downstream-divergence case (RFC #75 §13.5b) — the principal axis is the one most likely to drift silently because identity tends to creep in through a side door (an automation account, a second operator) before anyone updates the record."
```

## Selection procedure (§8)

Bidirectional check; chosen once. Recorded in the system architecture record;
code that grows ≥2 roles or a service principal in provenance while the record
still says singular is the `architecture_drift_gap` downstream-divergence case
(RFC #75 §13.5b).

## Composition (§5)

The principal is half of nearly every product: **provenance** (× temporality),
**access control** (× authz-granularity), **tenant membership** (× tenancy),
**audit log** (× temporality × authorization). Rules authored in 5A.3.
