# Identity Schema — Layer 1 Primitive # KNO Schema Version: 0.0.9 # # Changelog: # 0.0.2 — RFC-013 Identity Separation # - `id` field: Now ULID-only (no type prefix), immutable birth identity # - Added `slug` field: Human-readable name, mutable # - Added `name` field: Display name, mutable # - Added `provenance` field: Birth identity, content hash, assertions # - Deprecated `canonical_id` (superseded by ULID-only `id`) # - XRIs use ULID-only format: pspace://type:ULID # 0.0.1 — Initial Layer 1 identity schema # ============================================================================= # SCHEMA DECLARATION (RFC-007) # ============================================================================= $schema: kno@0.0.9 # ============================================================================= # IDENTITY (Layer 1 self-reference) # ============================================================================= id: 01KGK3V73G8RS1Y2Q67VKPDB2Q slug: identity-schema type: spec version: 0.0.2 # ============================================================================= # STANDARD TIER # ============================================================================= title: "Identity Schema" purpose: "Define the composable building block for entity identity" # ============================================================================= # RICH TIER — Relationships (Edge Maximization) # ============================================================================= provenance: origin: id: 01KGK3V73G8RS1Y2Q67VKPDB2Q timestamp: "2026-02-04T01:47:56Z" tool: manual-migration taxonomy: topics: - identity - entity-resolution - xri - federation - schema-composition keywords: - id - slug - name - provenance - local_ids - equiv_ids - xri - ulid - identity relationships: depends_on: - xri: "kno://specs/kno-spec" reason: "Defines the kno@0.0.9 schema this conforms to" related_to: - xri: "kno://specs/history-schema" reason: "Sibling Layer 1 primitive" - xri: "kno://specs/quality-schema" reason: "Sibling Layer 1 primitive" enables: - xri: "kno://specs/document-schema" reason: "Base type that composes this" - xri: "kno://specs/user-schema" reason: "Domain schema using identity" - xri: "kno://specs/organization-schema" reason: "Domain schema using identity" - xri: "kno://specs/possibility-schema" reason: "Domain schema using identity" implements: - xri: "kno://rfcs/RFC-002" reason: "Entity Resolution RFC" - xri: "pspace://rfc:rfc-013" reason: "Identity Separation RFC — three-layer identity model" quality: completeness: 0.95 last_reviewed: "2026-02-03" review_status: draft reviewed_by: "claude" # ============================================================================= # HISTORY (P9 Temporal) # ============================================================================= _history: version: 2 created: "2026-01-06T12:00:00Z" created_by: "claude" modified: "2026-02-03T12:00:00Z" modified_by: "claude" # ============================================================================= # SPECIFICATION CONTENT # ============================================================================= spec: status: Draft description: | Identity is a **Layer 1 primitive** — a composable building block that provides identity fields for any .kno entity. Every entity has an identity. Per **RFC-013 (Identity Separation)**, identity is structured as THREE distinct layers: | Layer | Field | Purpose | Mutable? | |-------|-------|---------|----------| | 1. Birth Identity | `id` | Immutable ULID assigned at creation | ❌ Never | | 2. Content Hash | `provenance.content.hash` | Integrity verification | ❌ Per-version | | 3. Name/Reference | `slug`, `name` | Human-readable identifier | ✅ Freely | This separation enables: - Arbitrary renaming without breaking XRI references - Content-addressed verification without infrastructure lock-in - Distributed trust via node assertions Additional identity fields: - **local_ids**: Human-friendly aliases (deprecated; use `slug`) - **equiv_ids**: External system identifiers (federation) - **provenance**: Birth ID, content hash, node assertions This schema is COMPOSED INTO higher-layer schemas like document-schema.kno, user.kno, and organization.kno. It is not used directly as a file type. ## Design Principles **P2 (Self-Contained):** Identity fields are defined here, not referenced from elsewhere. **P7 (Cooperative):** XRIs enable cross-entity relationships. **P9 (Temporal):** ULID encodes creation timestamp; provenance tracks history. **P10 (Derivable):** id (ULID) enables graph construction without manual linking. ## Composition Pattern Schemas that include identity declare: ```yaml composes: - identity@0.1 ``` This brings in all identity fields as required/optional per this spec. changelog: - version: "0.0.2" changes: - "RFC-013 Identity Separation implementation" - "id field now ULID-only (no type prefix), immutable birth identity" - "Added slug field (mutable, human-readable)" - "Added name field (mutable, display name)" - "Added provenance field (origin, content hash, assertions)" - "Deprecated canonical_id (superseded by ULID-only id)" - "Updated XRI format to pspace://type:ULID" - version: "0.0.1" changes: - "Initial Layer 1 identity schema" - "Extracted from kno-spec-schema.kno FEDERATED tier" - "Added composition pattern documentation" schema: # ------------------------------------------------------------------------- # REQUIRED IDENTITY FIELDS # ------------------------------------------------------------------------- required: fields: id: type: string format: "ulid | slug" pattern: "^([0-9A-Z]{26}|[a-z0-9]+(-[a-z0-9]+)*)$" immutable: true description: | Unique identifier. Per RFC-013, TWO valid formats: 1. **ULID (preferred for entities)**: Immutable birth identity - 26-character ULID assigned at creation - Encodes creation timestamp - NEVER changes, even if entity is renamed - Example: "01KG1FRSBANYFVF3S0C7GVKREY" 2. **Slug (for specs/schemas)**: Human-readable identifier - Kebab-case string - Example: "kno-spec", "user-schema" The `id` field is IMMUTABLE once assigned. To change how an entity is referenced by name, use the `slug` field. XRI format uses the id directly: pspace://type:id examples: - "01KG1FRSBANYFVF3S0C7GVKREY" - "kno-spec" - "user-schema" - "identity" note: "Required for ALL .kno files. IMMUTABLE once assigned." # ------------------------------------------------------------------------- # OPTIONAL IDENTITY FIELDS (FEDERATED tier) # ------------------------------------------------------------------------- optional: fields: # ----------------------------------------------------------------- # RFC-013 IDENTITY SEPARATION FIELDS # ----------------------------------------------------------------- slug: type: string format: kebab-case pattern: "^[a-z0-9]+(-[a-z0-9]+)*$" mutable: true description: | Human-readable identifier for URLs and references (RFC-013). Unlike `id` (immutable), `slug` can be changed freely without breaking XRI references. Multiple slugs can alias to the same entity via the resolution layer. Named XRI format: pspace://type:slug Example: pspace://playbook:structured-context-flow examples: - "structured-context-flow" - "max-engel" - "acme-corp" note: "Mutable. Use for human-friendly URLs and references." name: type: string mutable: true description: | Display name (can include spaces, special characters). Used for UI display when title is not appropriate. examples: - "Structured Context Flow (SCF)" - "Max Engel" - "Acme Corporation" note: "Mutable. For UI display." provenance: type: object description: | Origin, integrity, and verification information (RFC-013). Implements REQ-16 (Provenance & Audit) and REQ-17 (Validation & Assertion). fields: origin: type: object description: "Immutable creation metadata" fields: id: type: string format: ulid description: "ULID assigned at creation (same as top-level id)" timestamp: type: string format: date-time description: "ISO 8601 creation timestamp" emerged_from: type: [string, array] format: xri optional: true description: "XRI(s) of source entities this emerged from. Absent for axiomatic entities." tool: type: string optional: true description: "Software/tool that created the entity (e.g., pspace-cli, pspace-site)" content: type: object description: "Current version integrity verification" fields: hash: type: string description: "Content hash (algorithm:value format)" algorithm: type: string description: "Hash algorithm used (sha256, sha3-256, blake3)" normalized: type: boolean description: "Whether hash was computed on normalized form" assertions: type: array description: "Node attestations of entity validity" items: type: object fields: node: type: string format: xri description: "XRI of asserting node" timestamp: type: string format: date-time hash: type: string description: "Content hash the node verified" cid: type: string description: "IPFS CID if content-addressed" signature: type: string format: base64 description: "Cryptographic proof of assertion (optional)" # ----------------------------------------------------------------- # DEPRECATED FIELD (superseded by RFC-013) # ----------------------------------------------------------------- canonical_id: type: string format: prefixed-ulid pattern: "^[a-z]+_[0-9A-HJKMNP-TV-Z]{26}$" deprecated: true deprecated_by: "RFC-013" migration: "Use ULID-only `id` field instead. Extract ULID portion." description: | **DEPRECATED** — Superseded by RFC-013 identity separation. Previously: Immutable identifier with format {type_prefix}_{ulid} Now: Use ULID-only `id` field. Type is in $schema. Migration path: - Old: canonical_id: "usr_01HXYZ123ABC" - New: id: "01HXYZ123ABC" (ULID only) type: user (from $schema) slug: "max-engel" (human-readable) examples: - "usr_01H2XQJK9F5E8B7C3D4A6G" # pragma: allowlist secret - "org_01H2XQJK9F5E8B7C3D4A6G" # pragma: allowlist secret note: "DEPRECATED. Use ULID-only id field per RFC-013." local_ids: type: array items: type: string description: | Human-friendly aliases. Can change over time. Similar to XRI LocalID — the "friendly" name that may update. Use cases: - Username changes (old usernames become aliases) - Organization renames - Vanity URLs examples: - ["acme-corp", "acme", "acme-inc"] - ["max", "max-e", "maxe"] note: "First entry is current preferred alias" equiv_ids: type: array items: type: object fields: provider: type: string description: "External system provider identifier" examples: ["mercury", "github", "google_workspace", "stripe"] id: type: string description: "Identifier in that external system" examples: ["org_mercury_12345", "PossibilityInc", "workspace_abc"] priority: type: integer description: "Resolution priority (lower = preferred)" default: 10 verified: type: boolean description: "Whether this equivalence has been verified" default: false verified_at: type: string format: date-time description: "When verification was performed" description: | External identifiers from other systems (XRDS EquivID pattern). Enables federation across Mercury, GitHub, Google, etc. Federation allows: - Cross-system entity resolution - Unified identity across services - Import/export without ID translation examples: - provider: github id: PossibilityInc priority: 1 verified: true verified_at: "2026-01-01T00:00:00Z" note: "Based on XRDS EquivID pattern" canonical_equiv_id: type: string format: urn description: | A persistent external identifier that serves as the canonical external reference. Examples: EIN for US companies, DUNS number. Similar to XRDS CanonicalEquivID. examples: - "ein:12-3456789" - "duns:123456789" - "doi:10.1000/xyz123" note: "For entities with official external identifiers" # ------------------------------------------------------------------------- # XRI CONSTRUCTION # ------------------------------------------------------------------------- derived: xri: description: | XRI (eXtensible Resource Identifier) is DERIVED, not stored. Per RFC-013, multiple XRI formats are supported: | XRI Type | Format | Use Case | |----------|--------|----------| | **Identity XRI** | `pspace://type:ULID` | Stable forever, primary | | **Named XRI** | `pspace://type:slug` | Human-friendly alias | | **Version XRI** | `pspace://type:ULID/vX.Y.Z` | Specific version | | **Content XRI** | `pspace://type:ULID/sha256:...` | Content-verified | Examples: - pspace://user/01HXYZ123ABC (Identity XRI) - pspace://user:max-engel (Named XRI) - pspace://playbook/01KG1FRS.../v2.0.0 (Version XRI) XRIs are used in: - relationships.depends_on - relationships.related_to - Activity logging (actor/object) - Cross-references in content formula: "pspace://{type}/{id} OR pspace://{type}:{slug}" note: "Derived from type and id/slug fields; not stored" # =========================================================================== # COMPOSITION PATTERN # =========================================================================== composition: description: | Identity is a COMPOSABLE schema. Other schemas include identity fields by declaring composition. When a schema composes identity: - All required identity fields become required - All optional identity fields become available - XRI derivation applies automatically example: | # In document-schema.kno: composes: - identity@0.1 # Brings in id, canonical_id, local_ids, equiv_ids - history@0.1 - quality@0.1 # =========================================================================== # VALIDATION RULES # =========================================================================== validation: rules: - name: "id_required" severity: error message: "Every .kno file MUST have an id field" check: "exists(id)" - name: "id_format" severity: error message: "id must be ULID (26 uppercase alphanumeric) or kebab-case slug" check: "matches(id, '^([0-9A-Z]{26}|[a-z0-9]+(-[a-z0-9]+)*)$')" - name: "id_immutable" severity: error message: "id cannot change once set (RFC-013: birth identity is immutable)" check: "immutable(id)" note: "Checked via history comparison" - name: "slug_format" severity: error message: "slug must be kebab-case" check: "matches(slug, '^[a-z0-9]+(-[a-z0-9]+)*$')" when: "exists(slug)" - name: "canonical_id_deprecated" severity: warning message: "canonical_id is deprecated per RFC-013; use ULID-only id field" check: "not exists(canonical_id)" note: "Migration: extract ULID portion, use slug for human-readable name" - name: "equiv_ids_verified" severity: warning message: "Unverified equiv_ids should be verified before relying on them" check: "all(equiv_ids, verified == true)" when: "exists(equiv_ids)" # ============================================================================= # SOURCE (P4 — for this schema itself) # ============================================================================= source: | # This schema was extracted from the FEDERATED tier of kno-spec-schema.kno # and factored into a Layer 1 composable primitive. # # Original location: kno-spec-schema.kno > spec.schema.federated.fields.identity # # Design decision: Layer 1 primitives enable composition without # inheritance complexity. Schemas declare what they compose, and # validators merge the field definitions. source_format: yaml source_hash: "sha256:tbd" # ============================================================================= # CONTAINER TIER — Navigation Index # ============================================================================= _index: - path: "identity" line: 10 keywords: [id, type, version, basic-tier] - path: "spec/fields" line: 120 keywords: [id, canonical_id, local_ids, equiv_ids, xri] - path: "spec/composition" line: 200 keywords: [composes, composable, Layer-1, primitive] - path: "validation" line: 285 keywords: [rules, id_required, id_format, canonical_id] contains: - xri: "#identity" role: section title: "Identity Metadata" keywords: [id, type, version] - xri: "#spec" role: section title: "Specification Content" keywords: [fields, composition, validation] - xri: "#source" role: section title: "Source Provenance" keywords: [source, origin, extraction]