| 1 | #+TITLE: flexoentity
|
|---|
| 2 | #+SUBTITLE: A hardened entity base and deterministic identifier system for the Flex-O family
|
|---|
| 3 | #+AUTHOR: Enno
|
|---|
| 4 | #+DATE: 2025-10-20
|
|---|
| 5 | #+OPTIONS: toc:3 num:nil
|
|---|
| 6 |
|
|---|
| 7 | * Overview
|
|---|
| 8 |
|
|---|
| 9 | `flexoentity` provides the *identity and lifecycle backbone* for all Flex-O components
|
|---|
| 10 | (Flex-O-Grader, Flex-O-Vault, Flex-O-Drill, …).
|
|---|
| 11 |
|
|---|
| 12 | It defines how entities such as questions, media, catalogs, and exams are *identified, versioned, signed, and verified* — all without any external database dependencies.
|
|---|
| 13 |
|
|---|
| 14 | At its heart lie two modules:
|
|---|
| 15 |
|
|---|
| 16 | - =id_factory.py= – robust, cryptographically-verifiable *Flex-O ID generator*
|
|---|
| 17 | - =versioned_entity.py= – abstract *base class for all versioned entities*
|
|---|
| 18 |
|
|---|
| 19 | Together, they form a compact yet powerful foundation for audit-ready, reproducible data structures across offline and air-gapped deployments.
|
|---|
| 20 |
|
|---|
| 21 | ✳️ Design Goals
|
|---|
| 22 | |----------------|--------------------------------------------------------------------------------------------------------|
|
|---|
| 23 | | Goal | Description |
|
|---|
| 24 | |----------------|--------------------------------------------------------------------------------------------------------|
|
|---|
| 25 | | *Determinism* | IDs are derived from canonicalized entity content — identical input always yields identical ID prefix. |
|
|---|
| 26 | | *Integrity* | BLAKE2s hashing and digital signatures protect against manual tampering. |
|
|---|
| 27 | | *Traceability* | Version numbers (=@001A=, =@002S=, …) track entity lifecycle transitions. |
|
|---|
| 28 | | *Stability* | Hash prefixes remain constant across state changes; only version and state suffixes evolve. |
|
|---|
| 29 | | *Auditability* | Every entity can be serialized, verified, and reconstructed without hidden dependencies. |
|
|---|
| 30 | | *Simplicity* | Pure-Python, zero external libraries, self-contained and easy to embed. |
|
|---|
| 31 | |----------------|--------------------------------------------------------------------------------------------------------|
|
|---|
| 32 |
|
|---|
| 33 | * Flex-O ID Structure
|
|---|
| 34 |
|
|---|
| 35 | Each entity carries a unique *Flex-O ID*, generated by =FlexOID.generate()=.
|
|---|
| 36 |
|
|---|
| 37 | #+BEGIN_EXAMPLE
|
|---|
| 38 | AF-Q250101-9A4C2D@003S
|
|---|
| 39 | #+END_EXAMPLE
|
|---|
| 40 |
|
|---|
| 41 | |-----------|----------|---------------------------------------------|
|
|---|
| 42 | | Segment | Example | Meaning |
|
|---|
| 43 | |-----------|----------|---------------------------------------------|
|
|---|
| 44 | | *Domain* | =AF= | Logical scope (e.g. "Air Force") |
|
|---|
| 45 | | *Type* | =Q= | Entity type (e.g. Question) |
|
|---|
| 46 | | *Date* | =250101= | UTC creation date (YYMMDD) |
|
|---|
| 47 | | *Hash* | =9A4C2D= | 6-digit BLAKE2s digest of canonical content |
|
|---|
| 48 | | *Version* | =003= | Sequential version counter |
|
|---|
| 49 | | *State* | =S= | Lifecycle state (=D=, =A=, =S=, =P=, =O=) |
|
|---|
| 50 | |-----------|----------|---------------------------------------------|
|
|---|
| 51 |
|
|---|
| 52 | Hash collisions within a single session are automatically disambiguated (=-1=, =-2=, …).
|
|---|
| 53 |
|
|---|
| 54 | * Lifecycle States
|
|---|
| 55 |
|
|---|
| 56 | |-----------------------|---------|-----------------------------|
|
|---|
| 57 | | State | Abbrev. | Description |
|
|---|
| 58 | |-----------------------|---------|-----------------------------|
|
|---|
| 59 | | *DRAFT* | =D= | Editable, not yet validated |
|
|---|
| 60 | | *APPROVED* | =A= | Reviewed and accepted |
|
|---|
| 61 | | *APPROVED_AND_SIGNED* | =S= | Cryptographically signed |
|
|---|
| 62 | | *PUBLISHED* | =P= | Released to consumers |
|
|---|
| 63 | | *OBSOLETE* | =O= | Archived or replaced |
|
|---|
| 64 | |-----------------------|---------|-----------------------------|
|
|---|
| 65 |
|
|---|
| 66 | Transitions follow a strict progression:
|
|---|
| 67 | #+BEGIN_EXAMPLE
|
|---|
| 68 | DRAFT → APPROVED → APPROVED_AND_SIGNED → PUBLISHED → OBSOLETE
|
|---|
| 69 | #+END_EXAMPLE
|
|---|
| 70 |
|
|---|
| 71 | Version increments occur automatically for all *stable* transitions (APPROVED, SIGNED, PUBLISHED).
|
|---|
| 72 |
|
|---|
| 73 | * Core Classes
|
|---|
| 74 |
|
|---|
| 75 | ** =FlexOID=
|
|---|
| 76 |
|
|---|
| 77 | A lightweight immutable class representing the full identity of an entity.
|
|---|
| 78 |
|
|---|
| 79 | *Highlights*
|
|---|
| 80 | - =generate(domain, etype, estate, text, version=1)= → create a new ID
|
|---|
| 81 | - =next_version(oid)= → increment version safely
|
|---|
| 82 | - =clone_new_base(domain, etype, estate, text)= → start a new lineage
|
|---|
| 83 | - Deterministic prefix, state-dependent signature
|
|---|
| 84 |
|
|---|
| 85 | ** =FlexoEntity=
|
|---|
| 86 | Abstract base class for all versioned entities (e.g., Question, Exam, Catalog).
|
|---|
| 87 |
|
|---|
| 88 | Implements:
|
|---|
| 89 | - ID lifecycle management (=approve()=, =sign()=, =publish()=, =obsolete()=)
|
|---|
| 90 | - JSON serialization (=to_json()=, =from_json()=)
|
|---|
| 91 | - Integrity verification (=verify_integrity(entity)=)
|
|---|
| 92 | - Controlled state transitions with automatic timestamps
|
|---|
| 93 |
|
|---|
| 94 | Subclasses define a single property:
|
|---|
| 95 |
|
|---|
| 96 | #+BEGIN_SRC python
|
|---|
| 97 | @property
|
|---|
| 98 | def text_seed(self) -> str:
|
|---|
| 99 | """Canonical text or core content for hashing."""
|
|---|
| 100 | #+END_SRC
|
|---|
| 101 |
|
|---|
| 102 | * Integrity Verification
|
|---|
| 103 |
|
|---|
| 104 | Each entity can self-verify its integrity:
|
|---|
| 105 |
|
|---|
| 106 | #+BEGIN_SRC python
|
|---|
| 107 | entity = Question("AF", EntityType.QUESTION, "What is Ohm’s law?")
|
|---|
| 108 | json_str = entity.to_json()
|
|---|
| 109 | reloaded = Question.from_json(json_str)
|
|---|
| 110 |
|
|---|
| 111 | assert FlexoEntity.verify_integrity(reloaded)
|
|---|
| 112 | #+END_SRC
|
|---|
| 113 |
|
|---|
| 114 | If the file is tampered with (e.g. "Ohm’s" → "Omm’s"), verification fails:
|
|---|
| 115 |
|
|---|
| 116 | #+BEGIN_SRC python
|
|---|
| 117 | assert not FlexoEntity.verify_integrity(reloaded)
|
|---|
| 118 | #+END_SRC
|
|---|
| 119 |
|
|---|
| 120 | * Example
|
|---|
| 121 | #+BEGIN_SRC python
|
|---|
| 122 | from flexoentity.versioned_entity import FlexoEntity, EntityType, EntityState
|
|---|
| 123 |
|
|---|
| 124 | class Question(FlexoEntity):
|
|---|
| 125 | def __init__(self, domain, text):
|
|---|
| 126 | self._text = text
|
|---|
| 127 | super().__init__(domain, EntityType.QUESTION)
|
|---|
| 128 |
|
|---|
| 129 | @property
|
|---|
| 130 | def text_seed(self):
|
|---|
| 131 | return self._text
|
|---|
| 132 |
|
|---|
| 133 | * Usage
|
|---|
| 134 | #+BEGIN_SRC python
|
|---|
| 135 | q = Question("AF", "What is Ohm’s law?")
|
|---|
| 136 | print(q.flexo_id) # AF-Q250101-9A4C2D@001D
|
|---|
| 137 | q.approve()
|
|---|
| 138 | print(q.flexo_id) # AF-Q250101-9A4C2D@002A
|
|---|
| 139 | q.sign()
|
|---|
| 140 | print(q.flexo_id) # AF-Q250101-9A4C2D@003S
|
|---|
| 141 | #+END_SRC
|
|---|
| 142 |
|
|---|
| 143 | * JSON Example
|
|---|
| 144 | #+BEGIN_SRC json
|
|---|
| 145 | {
|
|---|
| 146 | "domain": "AF",
|
|---|
| 147 | "etype": "QUESTION",
|
|---|
| 148 | "text_seed": "What is Ohm’s law?",
|
|---|
| 149 | "state": "APPROVED_AND_SIGNED",
|
|---|
| 150 | "version": 3,
|
|---|
| 151 | "flexo_id": "AF-Q250101-9A4C2D@003S",
|
|---|
| 152 | "signature": "1E3A9F2A8B7C4D11",
|
|---|
| 153 | }
|
|---|
| 154 | #+END_SRC
|
|---|
| 155 |
|
|---|
| 156 | * Entity Type and State Codes
|
|---|
| 157 |
|
|---|
| 158 | |-------------|------|------------------------------|
|
|---|
| 159 | | EntityType | Code | Typical Use |
|
|---|
| 160 | |-------------|------|------------------------------|
|
|---|
| 161 | | QUESTION | Q | Exam question |
|
|---|
| 162 | | CATALOG | CAT | Question or media collection |
|
|---|
| 163 | | EXAM | EX | Composed test instance |
|
|---|
| 164 | | DATABASE | DB | Persistent store |
|
|---|
| 165 | | CERTIFICATE | CERT | Digital or approval document |
|
|---|
| 166 | |-------------|------|------------------------------|
|
|---|
| 167 |
|
|---|
| 168 | |---------------------|------|-------------------|
|
|---|
| 169 | | EntityState | Code | Meaning |
|
|---|
| 170 | |---------------------|------|-------------------|
|
|---|
| 171 | | DRAFT | D | Work in progress |
|
|---|
| 172 | | APPROVED | A | Reviewed |
|
|---|
| 173 | | APPROVED_AND_SIGNED | S | Signed version |
|
|---|
| 174 | | PUBLISHED | P | Publicly released |
|
|---|
| 175 | | OBSOLETE | O | Deprecated |
|
|---|
| 176 | |---------------------|------|-------------------|
|
|---|
| 177 |
|
|---|
| 178 | * Design Notes
|
|---|
| 179 | - *Hash Stability:* Only domain, entity type, and content text influence the hash.
|
|---|
| 180 | This ensures consistent prefixes across state changes.
|
|---|
| 181 | - *State-Dependent Signatures:* Each lifecycle stage has its own signature seed.
|
|---|
| 182 | Modifying a file without re-signing invalidates integrity.
|
|---|
| 183 | - *Obsolescence Threshold:* Version numbers above 900 trigger warnings;
|
|---|
| 184 | beyond 999 are considered obsolete.
|
|---|
| 185 | - *Clone Lineages:* Cloning an entity resets versioning but preserves metadata lineage.
|
|---|
| 186 |
|
|---|
| 187 | * Dependencies
|
|---|
| 188 | - Python 3.11+
|
|---|
| 189 | - Standard library only (=hashlib=, =json=, =datetime=, =enum=, =dataclasses=)
|
|---|
| 190 |
|
|---|
| 191 | No external packages. Fully compatible with *Guix*, *air-gapped* deployments, and *reproducible builds*.
|
|---|
| 192 |
|
|---|
| 193 | * Integration
|
|---|
| 194 | `flexoentity` is imported by higher-level modules such as:
|
|---|
| 195 |
|
|---|
| 196 | - *Flex-O-Grader* → manages question catalogs and exam bundles
|
|---|
| 197 | - *Flex-O-Vault* → provides persistent media storage with metadata integrity
|
|---|
| 198 | - *Flex-O-Drill* → uses versioned entities for training simulations
|
|---|
| 199 |
|
|---|
| 200 | All share the same identity and versioning logic — ensuring that
|
|---|
| 201 | *what was approved, signed, and published remains provably authentic.*
|
|---|
| 202 |
|
|---|
| 203 | * License
|
|---|
| 204 | MIT License 2025
|
|---|
| 205 | Part of the *Flex-O family* by Flex-O-Dyne GmbH
|
|---|
| 206 | Designed for reproducible, audit-ready, human-centered software.
|
|---|