Skip to main content

Document Identity and GitOps

In a modern infrastructure environment, "Configuration as Code" and GitOps are the gold standard. You want to define your application's state (like standard Roles, Print Formats, or System Settings) in declarative files, commit them to Git, and have a CI/CD pipeline apply them automatically to your database.

However, traditional database architectures fight against this when they use random UUIDs (uuid4) for primary keys.

The Random UUID Problem

Imagine you have a Role DocType and you export your "Administrator" role to a JSON file to seed new databases.

  1. On Monday, your CI/CD pipeline imports the JSON. The framework generates a random UUID (1234-abcd...) and inserts the row.
  2. On Tuesday, your pipeline runs again. The framework parses the JSON, generates a new random UUID (5678-efgh...), and tries to insert it.
  3. The database crashes with a DuplicateNameError (because the name "Administrator" already exists), or worse, it creates a duplicate role.

To solve this, Framework M introduces the Deterministic Document Identity Strategy.

Deterministic Document Identities (UUID5)

Instead of generating random identifiers, Framework M can generate a UUIDv5. A UUIDv5 is a deterministic hash: if you provide the exact same inputs for a document, you will always get the exact same UUID.

When you configure a DocType to use deterministic IDs:

class Role(BaseDocType):
class Meta:
id_strategy = "deterministic"

The DocumentIdService intercepts the save operation and calculates the ID by hashing the DocType Name and the document's unique Name field: hash("Role:Administrator")aac3687c-26c5-5914-9fe1-bc3970b68f8e

Seamless State Synchronization

With deterministic IDs, the GitOps flow becomes perfectly stable:

  1. Your pipeline imports the "Administrator" JSON.
  2. The framework calculates the deterministic UUID (aac3...).
  3. It checks the database. Because it knows the exact ID ahead of time, if aac3... already exists, it seamlessly routes the operation to an UPDATE instead of an INSERT.

Your state is successfully synchronized without writing custom "upsert" scripts or checking for duplicates manually.

The Universal Namespace

Generating a UUIDv5 requires a "root namespace". If Server A and Server B use different root namespaces, they will generate different UUIDs for the exact same data, defeating the purpose of portable GitOps exports.

In a closed-source enterprise app, developers often hardcode a random UUID string as their namespace. However, Framework M is open-source. Hardcoding a high-entropy string like "a1b2c3d4-e5f6..." in the codebase is an anti-pattern that triggers false positives in static analysis and secret scanners (like Gitleaks).

To solve this cleanly, Framework M dynamically computes its universal root namespace at runtime using Python's built-in URL namespace (uuid.NAMESPACE_URL + "framework-m.dev"). This guarantees that every installation of Framework M worldwide generates the exact same deterministic IDs, ensuring your declarative data exports are universally portable and secure.