Changeset d7499ca in flexoentity for README.md


Ignore:
Timestamp:
11/24/25 15:17:00 (7 weeks ago)
Author:
Enrico Schwass <ennoausberlin@…>
Branches:
master
Children:
2fd0536
Parents:
376e21b
Message:

Signature support for Linux and MacOS added

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 
    1027(Flex-O-Grader, Flex-O-Vault, Flex-O-Drill, …).
    1128
    12 It defines how entities such as questions, media, catalogs, and exams are *identified, versioned, signed, and verified* — all without any external database dependencies.
     29It defines how entities such as questions, media, catalogs, and exams are **identified, versioned, signed, and verified** — all without any external database dependencies.
    1330
    1431At its heart lie two modules:
    1532
    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**
    1835
    1936Together, they form a compact yet powerful foundation for audit-ready, reproducible data structures across offline and air-gapped deployments.
    2037
    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
     92Each 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. &ldquo;Air Force&rdquo;)</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>
    65206
    66207Transitions 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
     211Only 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
    76222
    77223A lightweight immutable class representing the full identity of an entity.
    78224
    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
    86237Abstract base class for all versioned entities (e.g., Question, Exam, Catalog).
    87238
    88239Implements:
    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
    93245
    94246Subclasses define a single property:
    95247
    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
    103256
    104257Each entity can self-verify its integrity:
    105258
    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
     265If the file is tampered with (e.g. &ldquo;Ohm’s&rdquo; → &ldquo;Omm’s&rdquo;), verification fails:
     266
     267
     268<a id="orge08e5d3"></a>
     269
     270# Real World Example
     271
     272Below you can see the implementation of a dedicated FlexoEntity class, used for Domains.
     273We set an ENTITY<sub>TYPE</sub> and define the needed fields in the data class. We define how to create
     274a default object, the text<sub>seed</sub> (it is easy because the domain id is unique and therefore sufficient)
     275and 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
    145373{
    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        &ldquo;flexo<sub>id</sub>&rdquo;: &ldquo;GEN<sub>GENERIC</sub>-D251124-29CE0F4BE59D@001S&rdquo;,
     375        &ldquo;fingerprint&rdquo;: &ldquo;534BD2EC5C5511F1&rdquo;,
     376        &ldquo;origin&rdquo;: &ldquo;GEN<sub>GENERIC</sub>-D251124-67C2CAE292CE@001D&rdquo;,
     377        &ldquo;originator<sub>id</sub>&rdquo;: &ldquo;00000000-0000-0000-0000-000000000000&rdquo;,
     378        &ldquo;owner<sub>id</sub>&rdquo;: &ldquo;00000000-0000-0000-0000-000000000000&rdquo;,
     379        &ldquo;domain<sub>id</sub>&rdquo;: &ldquo;GEN<sub>GENERIC</sub>&rdquo;,
     380        &ldquo;fullname&rdquo;: &ldquo;Generic Domain&rdquo;,
     381        &ldquo;description&rdquo;: &ldquo;&rdquo;,
     382        &ldquo;classification&rdquo;: &ldquo;UNCLASSIFIED&rdquo;
    153383}
    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
     565No 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
    199577
    200578All 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
    204586MIT License 2025
    205 Part of the *Flex-O family* by Flex-O-Dyne GmbH
     587Part of the **Flex-O family** by Flex-O-Dyne GmbH
    206588Designed for reproducible, audit-ready, human-centered software.
     589
Note: See TracChangeset for help on using the changeset viewer.