#+TITLE: flexoentity #+SUBTITLE: A hardened entity base and deterministic identifier system for the Flex-O family #+AUTHOR: Enno #+DATE: 2025-10-20 #+OPTIONS: toc:3 num:nil * Overview `flexoentity` provides the *identity and lifecycle backbone* for all Flex-O components (Flex-O-Grader, Flex-O-Vault, Flex-O-Drill, …). It defines how entities such as questions, media, catalogs, and exams are *identified, versioned, signed, and verified* — all without any external database dependencies. At its heart lie two modules: - =id_factory.py= – robust, cryptographically-verifiable *Flex-O ID generator* - =versioned_entity.py= – abstract *base class for all versioned entities* Together, they form a compact yet powerful foundation for audit-ready, reproducible data structures across offline and air-gapped deployments. ✳️ Design Goals |----------------|--------------------------------------------------------------------------------------------------------| | Goal | Description | |----------------|--------------------------------------------------------------------------------------------------------| | *Determinism* | IDs are derived from canonicalized entity content — identical input always yields identical ID prefix. | | *Integrity* | BLAKE2s hashing and digital signatures protect against manual tampering. | | *Traceability* | Version numbers (=@001A=, =@002S=, …) track entity lifecycle transitions. | | *Stability* | Hash prefixes remain constant across state changes; only version and state suffixes evolve. | | *Auditability* | Every entity can be serialized, verified, and reconstructed without hidden dependencies. | | *Simplicity* | Pure-Python, zero external libraries, self-contained and easy to embed. | |----------------|--------------------------------------------------------------------------------------------------------| * Flex-O ID Structure Each entity carries a unique *Flex-O ID*, generated by =FlexOID.generate()=. #+BEGIN_EXAMPLE AF-Q250101-9A4C2D@003S #+END_EXAMPLE |-----------|----------|---------------------------------------------| | Segment | Example | Meaning | |-----------|----------|---------------------------------------------| | *Domain* | =AF= | Logical scope (e.g. "Air Force") | | *Type* | =Q= | Entity type (e.g. Question) | | *Date* | =250101= | UTC creation date (YYMMDD) | | *Hash* | =9A4C2D= | 6-digit BLAKE2s digest of canonical content | | *Version* | =003= | Sequential version counter | | *State* | =S= | Lifecycle state (=D=, =A=, =S=, =P=, =O=) | |-----------|----------|---------------------------------------------| Hash collisions within a single session are automatically disambiguated (=-1=, =-2=, …). * Lifecycle States |-----------------------|---------|-----------------------------| | State | Abbrev. | Description | |-----------------------|---------|-----------------------------| | *DRAFT* | =D= | Editable, not yet validated | | *APPROVED* | =A= | Reviewed and accepted | | *APPROVED_AND_SIGNED* | =S= | Cryptographically signed | | *PUBLISHED* | =P= | Released to consumers | | *OBSOLETE* | =O= | Archived or replaced | |-----------------------|---------|-----------------------------| Transitions follow a strict progression: #+BEGIN_EXAMPLE DRAFT → APPROVED → APPROVED_AND_SIGNED → PUBLISHED → OBSOLETE #+END_EXAMPLE Version increments occur automatically for all *stable* transitions (APPROVED, SIGNED, PUBLISHED). * Core Classes ** =FlexOID= A lightweight immutable class representing the full identity of an entity. *Highlights* - =generate(domain, etype, estate, text, version=1)= → create a new ID - =next_version(oid)= → increment version safely - =clone_new_base(domain, etype, estate, text)= → start a new lineage - Deterministic prefix, state-dependent signature ** =FlexoEntity= Abstract base class for all versioned entities (e.g., Question, Exam, Catalog). Implements: - ID lifecycle management (=approve()=, =sign()=, =publish()=, =obsolete()=) - JSON serialization (=to_json()=, =from_json()=) - Integrity verification (=verify_integrity(entity)=) - Controlled state transitions with automatic timestamps Subclasses define a single property: #+BEGIN_SRC python @property def text_seed(self) -> str: """Canonical text or core content for hashing.""" #+END_SRC * Integrity Verification Each entity can self-verify its integrity: #+BEGIN_SRC python entity = Question("AF", EntityType.QUESTION, "What is Ohm’s law?") json_str = entity.to_json() reloaded = Question.from_json(json_str) assert FlexoEntity.verify_integrity(reloaded) #+END_SRC If the file is tampered with (e.g. "Ohm’s" → "Omm’s"), verification fails: #+BEGIN_SRC python assert not FlexoEntity.verify_integrity(reloaded) #+END_SRC * Example #+BEGIN_SRC python from flexoentity.versioned_entity import FlexoEntity, EntityType, EntityState class Question(FlexoEntity): def __init__(self, domain, text): self._text = text super().__init__(domain, EntityType.QUESTION) @property def text_seed(self): return self._text * Usage #+BEGIN_SRC python q = Question("AF", "What is Ohm’s law?") print(q.flexo_id) # AF-Q250101-9A4C2D@001D q.approve() print(q.flexo_id) # AF-Q250101-9A4C2D@002A q.sign() print(q.flexo_id) # AF-Q250101-9A4C2D@003S #+END_SRC * JSON Example #+BEGIN_SRC json { "domain": "AF", "etype": "QUESTION", "text_seed": "What is Ohm’s law?", "state": "APPROVED_AND_SIGNED", "version": 3, "flexo_id": "AF-Q250101-9A4C2D@003S", "signature": "1E3A9F2A8B7C4D11", "created_at": "2025-10-20T11:08:26Z", "updated_at": "2025-10-20T11:12:44Z" } #+END_SRC * Entity Type and State Codes |-------------|------|------------------------------| | EntityType | Code | Typical Use | |-------------|------|------------------------------| | QUESTION | Q | Exam question | | CATALOG | CAT | Question or media collection | | EXAM | EX | Composed test instance | | DATABASE | DB | Persistent store | | CERTIFICATE | CERT | Digital or approval document | |-------------|------|------------------------------| |---------------------|------|-------------------| | EntityState | Code | Meaning | |---------------------|------|-------------------| | DRAFT | D | Work in progress | | APPROVED | A | Reviewed | | APPROVED_AND_SIGNED | S | Signed version | | PUBLISHED | P | Publicly released | | OBSOLETE | O | Deprecated | |---------------------|------|-------------------| * Design Notes - *Hash Stability:* Only domain, entity type, and content text influence the hash. This ensures consistent prefixes across state changes. - *State-Dependent Signatures:* Each lifecycle stage has its own signature seed. Modifying a file without re-signing invalidates integrity. - *Obsolescence Threshold:* Version numbers above 900 trigger warnings; beyond 999 are considered obsolete. - *Clone Lineages:* Cloning an entity resets versioning but preserves metadata lineage. * Dependencies - Python 3.11+ - Standard library only (=hashlib=, =json=, =datetime=, =enum=, =dataclasses=) No external packages. Fully compatible with *Guix*, *air-gapped* deployments, and *reproducible builds*. * Integration `flexoentity` is imported by higher-level modules such as: - *Flex-O-Grader* → manages question catalogs and exam bundles - *Flex-O-Vault* → provides persistent media storage with metadata integrity - *Flex-O-Drill* → uses versioned entities for training simulations All share the same identity and versioning logic — ensuring that *what was approved, signed, and published remains provably authentic.* * License MIT License 2025 Part of the *Flex-O family* by Flex-O-Dyne GmbH Designed for reproducible, audit-ready, human-centered software.