Skip to content

Policy Template Versioning Architecture

Overview

GetCimple's policy versioning system enables us to continuously improve policy templates while customers maintain their configurations and implementation progress. The system uses a configuration-over-customization model that eliminates merge conflicts and provides automatic updates.

Key Innovation: Locked best-practice content + configurable parameters = Zero-conflict updates


Architectural Decision: Configuration vs Customization

The Core Choice

We had to decide: Do we let customers fully customize policy content, or do we limit them to configuration?

Decision: Configuration-based policies with locked content

Rationale

Option A: Full Customization (Rejected)

Model: Customers can edit any part of the policy

❌ Problems:

  • Merge conflicts when we release updates
  • Three-way diff required (our v1.0 β†’ their customized v1.0 β†’ our v1.1)
  • Complex UI for resolving conflicts
  • Can't guarantee compliance if content is modified
  • High support burden
  • Customers may introduce errors

Option B: Configuration-Based (Selected)

Model: Core content is locked, customers configure parameters

βœ… Benefits:

  • Zero merge conflicts (reapply configuration = auto-merge)
  • Simple UI (just configure new parameters)
  • Compliance guarantee (we control content)
  • Fast updates (minutes not hours)
  • Customers trust expert-maintained content
  • Aligns with industry practice (Vanta, Drata, Secureframe)

Real-World Validation

How policies are created today:

  1. Hire consultant ($5k-20k) β†’ Use their template, fill in specifics
  2. Download ISO/NIST template β†’ Configure for organization
  3. Write from scratch β†’ Almost nobody does this

Insight: Companies already use locked templates from experts. We're providing the same model as a service.


System Architecture

High-Level Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      GetCimple Maintains                     β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Policy Templates                                       β”‚ β”‚
β”‚  β”‚ β”œβ”€β”€ v1.0 (Released Jan 2025)                          β”‚ β”‚
β”‚  β”‚ β”œβ”€β”€ v1.1 (Released Jun 2025) ← New version           β”‚ β”‚
β”‚  β”‚ └── v2.0 (Released Dec 2025)                          β”‚ β”‚
β”‚  β”‚                                                        β”‚ β”‚
β”‚  β”‚ Content:                                               β”‚ β”‚
β”‚  β”‚ β€’ Locked sections (best practice text)                β”‚ β”‚
β”‚  β”‚ β€’ Variable definitions                                β”‚ β”‚
β”‚  β”‚ β€’ Parameter definitions (dropdowns, ranges)           β”‚ β”‚
β”‚  β”‚ β€’ Optional section definitions                        β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              ↓
                    Update Detection
                              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Customer Policies                       β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Active: Access Control Policy v1.0                     β”‚ β”‚
β”‚  β”‚ Configuration:                                         β”‚ β”‚
β”‚  β”‚   variables: {company_name: "Acme", ciso: "Jane"}     β”‚ β”‚
β”‚  β”‚   parameters: {password_length: 14, frequency: "Q"}   β”‚ β”‚
β”‚  β”‚   sections: ["cloud", "contractors"]                  β”‚ β”‚
β”‚  β”‚   addenda: "Our implementation timeline..."           β”‚ β”‚
β”‚  β”‚ Progress: 40% complete                                β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                              ↓                               β”‚
β”‚              Notification: "Update available"                β”‚
β”‚                              ↓                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Draft: Access Control Policy v1.1                      β”‚ β”‚
β”‚  β”‚ Auto-merged configuration:                             β”‚ β”‚
β”‚  β”‚   βœ“ Variables preserved                                β”‚ β”‚
β”‚  β”‚   βœ“ Parameters preserved (where still valid)           β”‚ β”‚
β”‚  β”‚   βœ“ Sections preserved (where still exist)             β”‚ β”‚
β”‚  β”‚   βœ“ Addenda preserved                                  β”‚ β”‚
β”‚  β”‚   ⚠️ New parameters: Need review                       β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                              ↓                               β”‚
β”‚                   Customer reviews & approves                β”‚
β”‚                              ↓                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Active: Access Control Policy v1.1 βœ“                   β”‚ β”‚
β”‚  β”‚ Archived: v1.0 (Jan-Jun 2025)                          β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Component Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     API Layer                            β”‚
β”‚                                                          β”‚
β”‚  GET  /api/policies/updates          β†’ Check updates    β”‚
β”‚  POST /api/policies/{id}/draft       β†’ Create draft     β”‚
β”‚  PATCH /api/policies/{id}/draft      β†’ Update config    β”‚
β”‚  POST /api/policies/{id}/draft/approve β†’ Activate       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Business Logic Layer                    β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ Update Detection Service                            β”‚β”‚
β”‚  β”‚ β€’ Query: customer version < latest version?         β”‚β”‚
β”‚  β”‚ β€’ Returns: List of available updates                β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ Auto-Merge Service                                  β”‚β”‚
β”‚  β”‚ β€’ Takes: Current config + New template              β”‚β”‚
β”‚  β”‚ β€’ Returns: Merged config with review items          β”‚β”‚
β”‚  β”‚ β€’ Logic: Reapply values where keys match            β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ Progress Mapping Service                            β”‚β”‚
β”‚  β”‚ β€’ Takes: Old progress + New template sections       β”‚β”‚
β”‚  β”‚ β€’ Returns: Mapped progress (by section ID)          β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ Validation Service                                  β”‚β”‚
β”‚  β”‚ β€’ Validates config against template definitions     β”‚β”‚
β”‚  β”‚ β€’ Checks parameter ranges, required fields          β”‚β”‚
β”‚  β”‚ β€’ Post-MVP: Dependency validation                   β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Data Layer                            β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ policy_templates     β”‚  β”‚ customer_policy_        β”‚  β”‚
β”‚  β”‚                      β”‚  β”‚ instances               β”‚  β”‚
β”‚  β”‚ β€’ current_version    β”‚  β”‚                         β”‚  β”‚
β”‚  β”‚ β€’ locked_content     β”‚  β”‚ β€’ active_version        β”‚  β”‚
β”‚  β”‚ β€’ variable_defs      β”‚  β”‚ β€’ configuration         β”‚  β”‚
β”‚  β”‚ β€’ parameter_defs     β”‚  β”‚ β€’ draft_version         β”‚  β”‚
β”‚  β”‚ β€’ section_defs       β”‚  β”‚ β€’ draft_configuration   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ policy_version_archives                              β”‚β”‚
β”‚  β”‚ β€’ Audit trail of previous versions                   β”‚β”‚
β”‚  β”‚ β€’ active_from / active_until dates                   β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Configuration Model Deep Dive

Policy Structure

Every policy template consists of four configuration layers:

1. Locked Content [NOT EDITABLE]

## Password Requirements

All user accounts at {{company_name}} must use passwords of at
least {{password_length}} characters. Passwords must be changed
every {{password_expiry_days}} days.

This requirement aligns with Essential Eight Maturity Level 2
for authentication hardening.

Characteristics:

  • Maintained by GetCimple compliance experts
  • Updated to reflect regulatory changes
  • Markdown with {{variable}} placeholders
  • Maps to compliance frameworks (E8, ISO 27001)

2. Variables [TEXT SUBSTITUTION]

{
  "company_name": "Acme Corporation",
  "ciso_name": "Jane Smith",
  "tool_name": "Okta"
}

Characteristics:

  • Simple key-value pairs
  • Always preserved during updates
  • Used for organization-specific names/tools

3. Parameters [BOUNDED CONFIGURATION]

{
  "password_length": 14, // Range: 12-16
  "password_expiry_days": 90, // Range: 60-180
  "review_frequency": "quarterly" // Options: monthly/quarterly/annually
}

Characteristics:

  • Bounded selections within best practices
  • Dropdowns, number ranges, dates
  • Smart migration: preserve if still valid, use default if not

4. Optional Sections [TOGGLE ON/OFF]

{
  "enabled_sections": ["cloud", "contractors", "byod"]
}

Characteristics:

  • Enable/disable based on applicability
  • Examples: Cloud services, Contractors, Remote work
  • Only kept if still exist in new version

5. Company-Specific Addenda [FREEFORM]

## Our Implementation Timeline

Phase 1 (Q1 2025): All administrative accounts
Phase 2 (Q2 2025): All user accounts

## Approved Exceptions

Legacy system X: To be migrated by Q3 2025

Characteristics:

  • Markdown freeform text
  • Rendered as formal appendix to policy
  • Always preserved during updates
  • Escape hatch for company context

Auto-Merge Algorithm

How Updates Work Without Conflicts

function autoMerge(
  oldConfig: Configuration,
  newTemplate: Template
): MergedConfiguration {
  return {
    // 1. Variables: Always carry forward (simple)
    variables: oldConfig.variables,

    // 2. Parameters: Smart migration
    parameters: mergeParameters(
      oldConfig.parameters,
      newTemplate.parameter_definitions
    ),

    // 3. Sections: Filter to still-valid
    enabled_sections: oldConfig.enabled_sections.filter(
      (sectionId) => newTemplate.section_definitions[sectionId] !== undefined
    ),

    // 4. Addenda: Always preserve
    company_addenda: oldConfig.company_addenda,
  }
}

function mergeParameters(old, newDefs) {
  const merged = {}

  for (const [key, definition] of Object.entries(newDefs)) {
    if (old[key] !== undefined && isValidValue(old[key], definition)) {
      // Parameter exists and still valid β†’ keep it
      merged[key] = old[key]
    } else {
      // Parameter new or invalid β†’ use default
      merged[key] = definition.default
    }
  }

  return merged
}

Why This Works

No conflicts possible because:

  1. Locked content can't conflict (customer never edited it)
  2. Variables are simple key-value (always preserve)
  3. Parameters are bounded (validate and default if needed)
  4. Sections are toggles (filter to valid IDs)
  5. Addenda is freeform (always append)

Result: 100% automatic merge success rate


Update Workflow States

State Machine

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Active    β”‚  Customer using v1.0
β”‚   v1.0      β”‚  Board-approved, implementing
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ We release v1.1
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Notified   β”‚  Dashboard: "Update available"
β”‚             β”‚  Customer can review anytime
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ Customer clicks "Review Update"
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Draft      β”‚  Auto-merged draft created
β”‚  Created    β”‚  v1.0 still active, v1.1 in draft
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ Customer configures new parameters
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Reviewing  β”‚  Customer reviews changes
β”‚             β”‚  Can edit draft configuration
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ Customer takes to board
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Approved   β”‚  Board approves v1.1
β”‚             β”‚  Ready to activate
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ System activates draft
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Active    β”‚  β”‚  Archived   β”‚
β”‚   v1.1      β”‚  β”‚  v1.0       β”‚
β”‚             β”‚  β”‚ (Jan-Jun 25)β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Database States

-- State 1: Active v1.0, no draft
active_template_version = '1.0'
draft_template_version = NULL
status = 'implementing'

-- State 2: Active v1.0, draft v1.1 in progress
active_template_version = '1.0'
draft_template_version = '1.1'
draft_configuration = {...}
status = 'implementing'

-- State 3: v1.1 activated, v1.0 archived
active_template_version = '1.1'
draft_template_version = NULL
status = 'approved'

-- Archive entry created:
INSERT INTO policy_version_archives (
  template_version = '1.0',
  active_from = '2025-01-15',
  active_until = '2025-06-15'
)

Implementation Progress Mapping

Challenge

When updating from v1.0 to v1.1:

  • v1.0 had 8 sections, 40% complete overall
  • v1.1 has 10 sections (2 new, 8 unchanged)
  • How do we map progress?

Solution: Section ID Matching

interface ProgressItem {
  section_id: string // 'password_requirements'
  status: 'not_started' | 'in_progress' | 'completed'
  progress_percentage: number
  evidence_ids: string[]
}

function mapProgress(
  oldProgress: ProgressItem[],
  newTemplate: Template
): ProgressItem[] {
  const newSections = newTemplate.locked_content.sections

  return newSections.map((section) => {
    const oldItem = oldProgress.find((p) => p.section_id === section.id)

    if (oldItem) {
      // Section exists in both versions β†’ carry forward
      return oldItem
    } else {
      // New section β†’ start from scratch
      return {
        section_id: section.id,
        status: 'not_started',
        progress_percentage: 0,
        evidence_ids: [],
      }
    }
  })
}

Result

v1.0 Progress:
β”œβ”€β”€ purpose: 100% βœ“
β”œβ”€β”€ scope: 100% βœ“
β”œβ”€β”€ password_requirements: 60% β†’
└── mfa_requirements: 0%

v1.1 Progress (after mapping):
β”œβ”€β”€ purpose: 100% βœ“ (carried forward)
β”œβ”€β”€ scope: 100% βœ“ (carried forward)
β”œβ”€β”€ password_requirements: 60% β†’ (carried forward)
β”œβ”€β”€ mfa_requirements: 0% (carried forward)
β”œβ”€β”€ biometric_auth: 0% [NEW]
└── session_management: 0% [NEW]

Security & Multi-Tenancy

Row Level Security (RLS)

-- Organizations can only see their own policies
CREATE POLICY org_isolation ON customer_policy_instances
  FOR ALL
  USING (organization_id = current_setting('app.current_organization_id')::UUID);

Data Isolation

Organization A                Organization B
β”œβ”€β”€ Policy Instance 1         β”œβ”€β”€ Policy Instance 10
β”‚   Template: Access Control  β”‚   Template: Access Control
β”‚   Version: 1.0              β”‚   Version: 1.1
β”‚   Config: {their data}      β”‚   Config: {their data}
└── Policy Instance 2         └── Policy Instance 11
    Template: Incident Resp       Template: BYOD Policy

Key Principle: Template is shared, configuration is isolated


Performance Considerations

Update Detection Query

-- Optimized with compound index
CREATE INDEX idx_customer_policies_updates
  ON customer_policy_instances(template_id, active_template_version);

-- Query executes in <10ms even with 10k policies
SELECT COUNT(*)
FROM customer_policy_instances
WHERE active_template_version < (
  SELECT current_version FROM policy_templates WHERE template_id = $1
);

Configuration Storage

JSONB Benefits:

  • Flexible schema for MVP
  • Fast queries with GIN indexes
  • Can add fields without migrations
  • Supports partial updates

Post-MVP Optimization:

  • Could normalize heavily-queried fields
  • Add materialized views for analytics
  • But MVP JSONB is perfectly fine

MVP vs Post-MVP Features

MVP (Essential)

βœ… Template versioning (current_version field) βœ… Update detection (version comparison) βœ… Auto-merge logic (reapply configuration) βœ… Dashboard notifications βœ… Simple review UI (changelog + new parameter config) βœ… Progress mapping (section ID matching) βœ… Archive previous versions

Post-MVP (Enhancements)

❌ Sophisticated diff viewer (side-by-side comparison) ❌ Email notifications (just dashboard for MVP) ❌ Policy health dashboard (outdated warnings) ❌ Complex validation rules (parameter dependencies) ❌ "Detach from template" escape hatch ❌ Deprecated section migration workflows ❌ Separate template_versions table (MVP uses single table)


Why This Must Be MVP

The Version Debt Trap

Without versioning from day one:

Day 1:  Customer adopts Access Control Policy
        β†’ Stored without template_version field
        β†’ Just "Access Control Policy" in database

Day 30: We improve policy content
        β†’ Can't tell which version customer has
        β†’ Can't provide targeted update

Day 60: 50 customers using various unknown versions
        β†’ Data migration nightmare
        β†’ Manual customer outreach required
        β†’ Trust damage

With versioning from day one:

Day 1:  Customer adopts Access Control Policy v1.0
        β†’ Stored: template_id + template_version: "1.0"

Day 30: We release v1.1
        β†’ Query: WHERE template_version < '1.1'
        β†’ Automatic notification to affected customers

Day 60: 50 customers: 30 on v1.0, 20 on v1.1
        β†’ Clear audit trail
        β†’ Targeted communications
        β†’ Smooth update path

We're Selling Managed Compliance

Without updates: We're a document generator With updates: We're a compliance platform

The ability to improve policies continuously is core to the value proposition.


Risks & Mitigations

Risk 1: Template Content Errors

Impact: High - Liability if policy is wrong Mitigation:

  • Rigorous internal review process
  • External compliance expert review
  • Start with well-vetted templates (ISO 27001, E8)
  • Version numbers allow rollback

Risk 2: Configuration Model Too Restrictive

Impact: Medium - Some customers need more Mitigation:

  • MVP: "Company Addenda" provides escape hatch
  • Post-MVP: "Detach" for true edge cases
  • Target market (SMB/mid-market) rarely needs full customization

Risk 3: Progress Mapping Edge Cases

Impact: Low - Incorrectly mapped progress Mitigation:

  • Conservative approach (only map exact section ID matches)
  • Clear UI showing what was carried forward
  • Manual override if needed

Future Enhancements

Phase 2: Advanced Features

Sophisticated Diff Viewer:

  • Side-by-side comparison
  • Highlight additions/deletions/modifications
  • Jump to changes navigation

Email Notifications:

  • Critical updates: Immediate email
  • Best practice: Weekly digest
  • Customizable notification preferences

Policy Health Dashboard:

  • Organization-wide policy compliance score
  • "12 of 13 policies up to date (92%)"
  • Red/yellow/green indicators

Validation Rules:

  • Parameter dependencies ("Biometric auth requires password_length β‰₯ 14")
  • Section prerequisites ("Cloud section requires Network section")
  • Custom validation logic

Phase 3: Enterprise Features

Detach from Template:

  • Full control for edge cases
  • Warning about losing updates
  • Rich text editor
  • Separate "Custom Policies" section

Template Versioning Table:

  • Full version history in separate table
  • Diff between any two versions
  • Rollback capability

Advanced Analytics:

  • Update adoption rates
  • Time-to-adopt metrics
  • Most-skipped updates
  • Customer segmentation

Conclusion

The configuration-based policy versioning system is foundational to GetCimple's value proposition. By choosing configuration over customization, we've created a system that is:

βœ… Simple - Customers configure, not rewrite βœ… Fast - Updates take minutes, not hours βœ… Reliable - Zero merge conflicts guaranteed βœ… Compliant - Expert-maintained content βœ… Scalable - Supports thousands of customers with one template update

This must be MVP because we're selling managed compliance, not document generation. The alternative (version debt) would create technical debt and trust damage that's difficult to recover from.

The lean MVP scope (simple UI, dashboard-only notifications, basic validation) gets us 80% of the value with 20% of the effort, while establishing the correct data foundation from day one.