- Timestamp:
- 11/24/25 15:17:00 (7 weeks ago)
- Branches:
- master
- Children:
- 2fd0536
- Parents:
- 376e21b
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
README.md
r376e21b rd7499ca 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 1 2 # Table of Contents 3 4 - [Overview](#org8360697) 5 - [Flex-O ID Structure](#org89396ad) 6 - [Lifecycle States](#orgd522c8c) 7 - [Core Classes](#org381a3f6) 8 - [FlexOID](#org6abdc86) 9 - [`FlexoEntity`](#org15b9543) 10 - [Integrity Verification](#org1617277) 11 - [Real World Example](#orge08e5d3) 12 - [Usage](#orgc8e7dd2) 13 - [Serialization Example](#org1b3bbed) 14 - [Entity Type and State Codes](#org5dfc109) 15 - [Design Notes](#org659c733) 16 - [Dependencies](#orga67f3a6) 17 - [Integration](#org10ab165) 18 - [License](#org48ba119) 19 20 21 22 <a id="org8360697"></a> 23 24 # Overview 25 26 \`flexoentity\` provides the **identity and lifecycle backbone** for all Flex-O components 10 27 (Flex-O-Grader, Flex-O-Vault, Flex-O-Drill, …). 11 28 12 It defines how entities such as questions, media, catalogs, and exams are * identified, versioned, signed, and verified* — all without any external database dependencies.29 It defines how entities such as questions, media, catalogs, and exams are **identified, versioned, signed, and verified** — all without any external database dependencies. 13 30 14 31 At its heart lie two modules: 15 32 16 - =id_factory.py= – robust, cryptographically-verifiable *Flex-O ID generator*17 - =versioned_entity.py= – abstract *base class for all versioned entities*33 - `id_factory.py` – robust, cryptographically-verifiable **Flex-O ID generator** 34 - `flexo_entity.py` – abstract **base class for all versioned entities** 18 35 19 36 Together, they form a compact yet powerful foundation for audit-ready, reproducible data structures across offline and air-gapped deployments. 20 37 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 |-----------------------|---------|-----------------------------| 38 - Design Goals 39 40 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> 41 42 43 <colgroup> 44 <col class="org-left" /> 45 46 <col class="org-left" /> 47 </colgroup> 48 <thead> 49 <tr> 50 <th scope="col" class="org-left">Goal</th> 51 <th scope="col" class="org-left">Description</th> 52 </tr> 53 </thead> 54 <tbody> 55 <tr> 56 <td class="org-left"><b>Determinism</b></td> 57 <td class="org-left">IDs are derived from canonicalized entity content — identical input always yields identical ID prefix.</td> 58 </tr> 59 60 <tr> 61 <td class="org-left"><b>Integrity</b></td> 62 <td class="org-left">BLAKE2s hashing and digital signatures protect against manual tampering.</td> 63 </tr> 64 65 <tr> 66 <td class="org-left"><b>Traceability</b></td> 67 <td class="org-left">Version numbers (<code>@001A</code>, <code>@002S</code>, …) track entity lifecycle transitions.</td> 68 </tr> 69 70 <tr> 71 <td class="org-left"><b>Stability</b></td> 72 <td class="org-left">Hash prefixes remain constant across state changes; only version and state suffixes evolve.</td> 73 </tr> 74 75 <tr> 76 <td class="org-left"><b>Auditability</b></td> 77 <td class="org-left">Every entity can be serialized, verified, and reconstructed without hidden dependencies.</td> 78 </tr> 79 80 <tr> 81 <td class="org-left"><b>Simplicity</b></td> 82 <td class="org-left">Pure-Python, zero external libraries, self-contained and easy to embed.</td> 83 </tr> 84 </tbody> 85 </table> 86 87 88 <a id="org89396ad"></a> 89 90 # Flex-O ID Structure 91 92 Each entity carries a unique **Flex-O ID**, generated by `FlexOID.generate()`. 93 94 AF-I250101-9A4C2D@003S 95 96 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> 97 98 99 <colgroup> 100 <col class="org-left" /> 101 102 <col class="org-left" /> 103 104 <col class="org-left" /> 105 </colgroup> 106 <thead> 107 <tr> 108 <th scope="col" class="org-left">Segment</th> 109 <th scope="col" class="org-left">Example</th> 110 <th scope="col" class="org-left">Meaning</th> 111 </tr> 112 </thead> 113 <tbody> 114 <tr> 115 <td class="org-left"><b>Domain</b></td> 116 <td class="org-left"><code>AF or PY_LANG</code></td> 117 <td class="org-left">Uppercase - Logical scope (e.g. “Air Force”)</td> 118 </tr> 119 120 <tr> 121 <td class="org-left"><b>Type</b></td> 122 <td class="org-left"><code>I</code></td> 123 <td class="org-left">Entity type (e.g. ITEM)</td> 124 </tr> 125 126 <tr> 127 <td class="org-left"><b>Date</b></td> 128 <td class="org-left"><code>250101</code></td> 129 <td class="org-left">UTC creation date (YYMMDD)</td> 130 </tr> 131 132 <tr> 133 <td class="org-left"><b>Hash</b></td> 134 <td class="org-left"><code>9A4C2D4F6E53</code></td> 135 <td class="org-left">12-digit BLAKE2s digest of canonical content</td> 136 </tr> 137 138 <tr> 139 <td class="org-left"><b>Version</b></td> 140 <td class="org-left"><code>003</code></td> 141 <td class="org-left">Sequential version counter</td> 142 </tr> 143 144 <tr> 145 <td class="org-left"><b>State</b></td> 146 <td class="org-left"><code>S</code></td> 147 <td class="org-left">Lifecycle state (D, A, S, P, O)</td> 148 </tr> 149 </tbody> 150 </table> 151 152 153 <a id="orgd522c8c"></a> 154 155 # Lifecycle States 156 157 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> 158 159 160 <colgroup> 161 <col class="org-left" /> 162 163 <col class="org-left" /> 164 165 <col class="org-left" /> 166 </colgroup> 167 <thead> 168 <tr> 169 <th scope="col" class="org-left">State</th> 170 <th scope="col" class="org-left">Abbrev.</th> 171 <th scope="col" class="org-left">Description</th> 172 </tr> 173 </thead> 174 <tbody> 175 <tr> 176 <td class="org-left"><b>DRAFT</b></td> 177 <td class="org-left"><code>D</code></td> 178 <td class="org-left">Editable, not yet validated</td> 179 </tr> 180 181 <tr> 182 <td class="org-left"><b>APPROVED</b></td> 183 <td class="org-left"><code>A</code></td> 184 <td class="org-left">Reviewed and accepted</td> 185 </tr> 186 187 <tr> 188 <td class="org-left"><b>APPROVED<sub>AND</sub><sub>SIGNED</sub></b></td> 189 <td class="org-left"><code>S</code></td> 190 <td class="org-left">Cryptographically signed</td> 191 </tr> 192 193 <tr> 194 <td class="org-left"><b>PUBLISHED</b></td> 195 <td class="org-left"><code>P</code></td> 196 <td class="org-left">Released to consumers</td> 197 </tr> 198 199 <tr> 200 <td class="org-left"><b>OBSOLETE</b></td> 201 <td class="org-left"><code>O</code></td> 202 <td class="org-left">Archived or replaced</td> 203 </tr> 204 </tbody> 205 </table> 65 206 66 207 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= 208 209 DRAFT -> APPROVED -> APPROVED_AND_SIGNED -> PUBLISHED -> OBSOLETE 210 211 Only DRAFT entities can be deleted - all others got OBSOLETE mark instead 212 213 214 <a id="org381a3f6"></a> 215 216 # Core Classes 217 218 219 <a id="org6abdc86"></a> 220 221 ## FlexOID 76 222 77 223 A lightweight immutable class representing the full identity of an entity. 78 224 79 *Highlights* 80 - =generate(domain, entity_type, estate, text, version=1)= → create a new ID 81 - =next_version(oid)= → increment version safely 82 - =clone_new_base(domain, entity_type, estate, text)= → start a new lineage 83 - Deterministic prefix, state-dependent signature 84 85 ** =FlexoEntity= 225 **Highlights** 226 227 - safe<sub>generate</sub>(domain, entity<sub>type</sub>, estate, text, version=1, repo) -> create a new ID 228 - next<sub>version</sub>(oid) -> increment version safely 229 - clone<sub>new</sub><sub>base</sub>(domain, entity<sub>type</sub>, estate, text) -> start a new lineage 230 - Deterministic prefix, state-dependent signature 231 232 233 <a id="org15b9543"></a> 234 235 ## `FlexoEntity` 236 86 237 Abstract base class for all versioned entities (e.g., Question, Exam, Catalog). 87 238 88 239 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 240 241 - ID lifecycle management (approve(), sign(), publish(), obsolete()) 242 - Serialization (to<sub>json</sub>(), from<sub>json</sub>(), to<sub>dict</sub>(), from<sub>dict</sub>()) 243 - Integrity verification (verify<sub>integrity</sub>(entity)) 244 - Controlled state transitions with automatic timestamps 93 245 94 246 Subclasses define a single property: 95 247 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 248 @property 249 def text_seed(self) -> str: 250 """Canonical text or core content for hashing.""" 251 252 253 <a id="org1617277"></a> 254 255 # Integrity Verification 103 256 104 257 Each entity can self-verify its integrity: 105 258 106 #+BEGIN_SRC python 107 entity = Question("AF", EntityType.ITEM, "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.ITEM) 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 259 entity = Question.with_domain_id(domain_id="AF", text="What is Ohm’s law?", topic="Electronics") 260 json_str = entity.to_json() 261 reloaded = Question.from_json(json_str) 262 263 assert FlexoEntity.verify_integrity(reloaded) 264 265 If the file is tampered with (e.g. “Ohm’s” → “Omm’s”), verification fails: 266 267 268 <a id="orge08e5d3"></a> 269 270 # Real World Example 271 272 Below you can see the implementation of a dedicated FlexoEntity class, used for Domains. 273 We set an ENTITY<sub>TYPE</sub> and define the needed fields in the data class. We define how to create 274 a default object, the text<sub>seed</sub> (it is easy because the domain id is unique and therefore sufficient) 275 and the methods for serialization. 276 277 from uuid import UUID 278 from dataclasses import dataclass 279 from flexoentity import FlexOID, FlexoEntity, EntityType 280 281 @dataclass 282 class Domain(FlexoEntity): 283 """ 284 I am a helper class to provide more information than just a 285 domain abbreviation in FlexOID, doing mapping and management 286 """ 287 288 ENTITY_TYPE = EntityType.DOMAIN 289 290 fullname: str = "" 291 description: str = "" 292 classification: str = "UNCLASSIFIED" 293 294 @classmethod 295 def default(cls): 296 """Return the default domain object.""" 297 return cls.with_domain_id(domain_id="GEN_GENERIC", 298 fullname="Generic Domain", classification="UNCLASSIFIED") 299 300 @property 301 def text_seed(self) -> str: 302 return self.domain_id 303 304 def to_dict(self): 305 base = super().to_dict() 306 base.update({ 307 "flexo_id": self.flexo_id, 308 "domain_id": self.domain_id, 309 "fullname": self.fullname, 310 "description": self.description, 311 "classification": self.classification, 312 }) 313 return base 314 315 @classmethod 316 def from_dict(cls, data): 317 # Must have flexo_id 318 if "flexo_id" not in data: 319 raise ValueError("Domain serialization missing 'flexo_id'.") 320 321 flexo_id = FlexOID(data["flexo_id"]) 322 323 obj = cls( 324 fullname=data.get("fullname", ""), 325 description=data.get("description", ""), 326 classification=data.get("classification", "UNCLASSIFIED"), 327 flexo_id=flexo_id, 328 _in_factory=True 329 ) 330 331 # Restore metadata 332 obj.origin = data.get("origin") 333 obj.fingerprint = data.get("fingerprint", "") 334 obj.originator_id = ( 335 UUID(data["originator_id"]) if data.get("originator_id") else None 336 ) 337 obj.owner_id = ( 338 UUID(data["owner_id"]) if data.get("owner_id") else None 339 ) 340 341 return obj 342 343 344 <a id="orgc8e7dd2"></a> 345 346 # Usage 347 348 d = Domain.default() 349 print(d.flexo_id) # GEN_GENERIC-D251124-67C2CAE292CE@001D 350 d.approve() 351 print(d.flexo_id) # GEN_GENERIC-D251124-67C2CAE292CE@001A 352 d.sign() 353 print(d.flexo_id) # GEN_GENERIC-D251124-67C2CAE292CE@001S 354 355 356 <a id="org1b3bbed"></a> 357 358 # Serialization Example 359 360 { 361 'flexo_id': FlexOID(GEN_GENERIC-D251124-29CE0F4BE59D@001S), 362 'fingerprint': '534BD2EC5C5511F1', 363 'origin': FlexOID(GEN_GENERIC-D251124-67C2CAE292CE@001D), 364 'originator_id': '00000000-0000-0000-0000-000000000000', 365 'owner_id': '00000000-0000-0000-0000-000000000000', 366 'domain_id': 'GEN_GENERIC', 367 'fullname': 'Generic Domain', 368 'description': '', 369 'classification': 'UNCLASSIFIED'} 370 371 \#+BEGIN<sub>SRC</sub> js 372 145 373 { 146 "domain": "AF", 147 "entity_type": "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", 374 “flexo<sub>id</sub>”: “GEN<sub>GENERIC</sub>-D251124-29CE0F4BE59D@001S”, 375 “fingerprint”: “534BD2EC5C5511F1”, 376 “origin”: “GEN<sub>GENERIC</sub>-D251124-67C2CAE292CE@001D”, 377 “originator<sub>id</sub>”: “00000000-0000-0000-0000-000000000000”, 378 “owner<sub>id</sub>”: “00000000-0000-0000-0000-000000000000”, 379 “domain<sub>id</sub>”: “GEN<sub>GENERIC</sub>”, 380 “fullname”: “Generic Domain”, 381 “description”: “”, 382 “classification”: “UNCLASSIFIED” 153 383 } 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 384 385 386 <a id="org5dfc109"></a> 387 388 # Entity Type and State Codes 389 390 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> 391 392 393 <colgroup> 394 <col class="org-left" /> 395 396 <col class="org-left" /> 397 398 <col class="org-left" /> 399 </colgroup> 400 <thead> 401 <tr> 402 <th scope="col" class="org-left">EntityType</th> 403 <th scope="col" class="org-left">Code</th> 404 <th scope="col" class="org-left">Typical Use</th> 405 </tr> 406 </thead> 407 <tbody> 408 <tr> 409 <td class="org-left">GENERIC</td> 410 <td class="org-left">G</td> 411 <td class="org-left">Generic entities that does not fit other types yet or are temporarily only</td> 412 </tr> 413 414 <tr> 415 <td class="org-left">DOMAIN</td> 416 <td class="org-left">D</td> 417 <td class="org-left">Every Domain is of this type</td> 418 </tr> 419 420 <tr> 421 <td class="org-left">MEDIA</td> 422 <td class="org-left">M</td> 423 <td class="org-left">Every media item belongs to this type, e.g. Pictures, Audio, Video</td> 424 </tr> 425 426 <tr> 427 <td class="org-left">ITEM</td> 428 <td class="org-left">I</td> 429 <td class="org-left">An Entity what is usually used in a collection, e.g. Questions in a test</td> 430 </tr> 431 432 <tr> 433 <td class="org-left">COLLECTION</td> 434 <td class="org-left">C</td> 435 <td class="org-left">A collection of items, as an Exam or a catalog</td> 436 </tr> 437 438 <tr> 439 <td class="org-left">TEXT</td> 440 <td class="org-left">T</td> 441 <td class="org-left">A text document</td> 442 </tr> 443 444 <tr> 445 <td class="org-left">HANDOUT</td> 446 <td class="org-left">H</td> 447 <td class="org-left">A published document</td> 448 </tr> 449 450 <tr> 451 <td class="org-left">OUTPUT</td> 452 <td class="org-left">O</td> 453 <td class="org-left">The output of a computation</td> 454 </tr> 455 456 <tr> 457 <td class="org-left">RECORD</td> 458 <td class="org-left">R</td> 459 <td class="org-left">Record type data, as bibliography entries</td> 460 </tr> 461 462 <tr> 463 <td class="org-left">SESSION</td> 464 <td class="org-left">S</td> 465 <td class="org-left">A unique session, e.g. managed by a session manager</td> 466 </tr> 467 468 <tr> 469 <td class="org-left">USER</td> 470 <td class="org-left">U</td> 471 <td class="org-left">User objects</td> 472 </tr> 473 474 <tr> 475 <td class="org-left">CONFIG</td> 476 <td class="org-left">F</td> 477 <td class="org-left">CONFIG files that need to be tracked over time and state</td> 478 </tr> 479 480 <tr> 481 <td class="org-left">EVENT</td> 482 <td class="org-left">E</td> 483 <td class="org-left">Events that have to be tracked over time, as status messages or orders</td> 484 </tr> 485 486 <tr> 487 <td class="org-left">ATTESTATION</td> 488 <td class="org-left">X</td> 489 <td class="org-left">Entities that attest a formal technical (not human) check e.g. Signatures</td> 490 </tr> 491 </tbody> 492 </table> 493 494 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> 495 496 497 <colgroup> 498 <col class="org-left" /> 499 500 <col class="org-left" /> 501 502 <col class="org-left" /> 503 </colgroup> 504 <thead> 505 <tr> 506 <th scope="col" class="org-left">EntityState</th> 507 <th scope="col" class="org-left">Code</th> 508 <th scope="col" class="org-left">Meaning</th> 509 </tr> 510 </thead> 511 <tbody> 512 <tr> 513 <td class="org-left">DRAFT</td> 514 <td class="org-left">D</td> 515 <td class="org-left">Work in progress</td> 516 </tr> 517 518 <tr> 519 <td class="org-left">APPROVED</td> 520 <td class="org-left">A</td> 521 <td class="org-left">Reviewed</td> 522 </tr> 523 524 <tr> 525 <td class="org-left">APPROVED<sub>AND</sub><sub>SIGNED</sub></td> 526 <td class="org-left">S</td> 527 <td class="org-left">Signed version</td> 528 </tr> 529 530 <tr> 531 <td class="org-left">PUBLISHED</td> 532 <td class="org-left">P</td> 533 <td class="org-left">Publicly released</td> 534 </tr> 535 536 <tr> 537 <td class="org-left">OBSOLETE</td> 538 <td class="org-left">O</td> 539 <td class="org-left">Deprecated</td> 540 </tr> 541 </tbody> 542 </table> 543 544 545 <a id="org659c733"></a> 546 547 # Design Notes 548 549 - **Hash Stability:** Only domain, entity type, and content text influence the hash. 550 This ensures consistent prefixes across state changes. 551 - **State-Dependent Signatures:** Each lifecycle stage has its own signature seed. 552 Modifying a file without re-signing invalidates integrity. 553 - **Obsolescence Threshold:** Version numbers above 900 trigger warnings; 554 beyond 999 are considered obsolete. 555 - **Clone Lineages:** Cloning an entity resets versioning but preserves metadata lineage. 556 557 558 <a id="orga67f3a6"></a> 559 560 # Dependencies 561 562 - Python 3.11+ 563 - Standard library only (`hashlib`, `json`, `datetime`, `enum`, `dataclasses`) 564 565 No external packages. Fully compatible with **Guix**, **air-gapped** deployments, and **reproducible builds**. 566 567 568 <a id="org10ab165"></a> 569 570 # Integration 571 572 \`flexoentity\` is imported by higher-level modules such as: 573 574 - **Flex-O-Grader** → manages question catalogs and exam bundles 575 - **Flex-O-Vault** → provides persistent media storage with metadata integrity 576 - **Flex-O-Drill** → uses versioned entities for training simulations 199 577 200 578 All share the same identity and versioning logic — ensuring that 201 *what was approved, signed, and published remains provably authentic.* 202 203 * License 579 **what was approved, signed, and published remains provably authentic.** 580 581 582 <a id="org48ba119"></a> 583 584 # License 585 204 586 MIT License 2025 205 Part of the * Flex-O family* by Flex-O-Dyne GmbH587 Part of the **Flex-O family** by Flex-O-Dyne GmbH 206 588 Designed for reproducible, audit-ready, human-centered software. 589
Note:
See TracChangeset
for help on using the changeset viewer.
