Skip to content

Data Model Reference

Overview

This document consolidates all data structures used across noorm. It serves as a single reference for types, database schemas, file formats, and their relationships.

noorm separates data into three tiers:

TierStorageEncryptionVersioned
State.noorm/state/state.encAES-256-GCMGit-ignored
Settings.noorm/settings.ymlNoneCommitted
DatabaseTarget databaseN/ATracked in-db

State holds secrets and credentials. Settings holds team-shared rules. Database tables track execution history.

Entity Relationship Diagram


State (Encrypted)

State File

The encrypted state file at .noorm/state/state.enc contains all sensitive configuration.

FieldTypeDescription
versionstringPackage version that last saved this state
schemaVersionnumberState schema version, driving the core/version/state migrations
knownUsersMapKnown users discovered from databases (identityHash → KnownUser)
activeConfigstring | nullCurrently selected config name
configsMapDatabase configurations by name
secretsMapConfig-scoped secrets (configName → key → value)
globalSecretsMapApp-level secrets shared across configs

The cryptographic identity is not in state—it lives in ~/.noorm/ (see Key Files). State migration v1 still writes a vestigial identity: null key onto older files; nothing reads it.

Encrypted Payload

On-disk format for the state file.

FieldTypeDescription
algorithmstringAlways aes-256-gcm; rejected at decrypt if anything else
kdfstring?Key derivation used. Absent on payloads written before the field existed, which are hkdf-sha256 by definition
ivstringInitialization vector (base64)
authTagstringAuthentication tag (base64)
ciphertextstringEncrypted state JSON (base64)

The AES key derives from the user's private key (~/.noorm/identity.key) via HKDF-SHA256. Recording kdf is what makes the derivation changeable later—without it, a build that changed the derivation would report every existing state file as "wrong key or corrupted".


Configuration

Config

A database connection profile stored in encrypted state.

FieldTypeRequiredDescription
namestringYesUnique identifier (e.g., dev, staging, prod)
typeenumYeslocal or remote
isTestbooleanYesMarks database as disposable for testing
accessConfigAccessYesPer-channel access roles ({ user, agent }) — replaces the legacy protected boolean
connectionConnectionConfigYesDatabase connection details
identitystringNoOverride identity for executed_by field

File system paths are not on the config—they come from settings (paths.sql, paths.changes; see PathConfig).

ConfigAccess

Per-channel access grant. Role is 'viewer' | 'operator' | 'admin'; Channel is 'user' | 'agent'.

FieldTypeDescription
userRoleA human at the keyboard—CLI, TUI, or SDK
agentRole | falseAn AI agent, whichever binary it reached for. false hides the config entirely on this channel

The channel describes who is driving, not which transport was used: an agent that shells out to the CLI after an MCP refusal is still agent. The field was named mcp until state migration v3 renamed it to agent.

checkPolicy(channel, config, permission) resolves a ConfigAccess + Permission into allow/confirm/deny, channel-aware (confirm prompts on user, collapses to deny on agent). See docs/spec/config-access-roles.md for the full permission matrix.

ConnectionConfig

Database connection parameters.

FieldTypeRequiredDescription
dialectenumYespostgres, mysql, sqlite, or mssql
hoststringNetworkHostname (required for non-SQLite)
portnumberNoPort number (defaults by dialect)
databasestringYesDatabase name
filenamestringSQLiteFile path for SQLite databases
userstringNoDatabase username
passwordstringNoDatabase password
sslboolean or objectNoSSL/TLS configuration (rejectUnauthorized, ca, cert, key)
poolobjectNoConnection pool settings (min, max)
tlsServerNamestringNoHostname the server's TLS certificate is issued for. Needed only when host is an IP address, since SNI cannot carry an IP literal. MSSQL is the only dialect that reads it

Default ports by dialect:

DialectDefault Port
postgres5432
mysql3306
mssql1433
sqliteN/A

ConfigSummary

Lightweight config view for listings. Omits credentials.

FieldTypeDescription
namestringConfig identifier
typeenumlocal or remote
isTestbooleanTest database flag
accessConfigAccessPer-channel access roles
isActivebooleanCurrently selected config
dialectDialectpostgres, mysql, sqlite, or mssql
databasestringDatabase name

Config Resolution Order

Configs merge from five sources in priority order:

CLI flags > Environment > Stored config > Stage defaults > Defaults

Higher priority sources override lower ones, enabling flexible overrides for CI/CD.


Settings

Settings File

The .noorm/settings.yml file configures project-wide behavior. Unlike state, this file is not encrypted and should be version controlled.

yaml
# .noorm/settings.yml
build:
    include:
        - tables/**/*.sql
        - views/**/*.sql
    exclude:
        - '**/*.test.sql'

paths:
    sql: db/sql
    changes: db/changes

stages:
    dev:
        description: Development database
        defaults:
            dialect: postgres
            isTest: true
    prod:
        description: Production database
        locked: true
        defaults:
            dialect: postgres
            protected: true   # access ceiling: clamps resolved access to at most operator/viewer
        secrets:
            - key: DB_PASSWORD
              type: password
              required: true

rules:
    - match:
          protected: true   # matches guarded(config), i.e. access.user !== 'admin'
      exclude:
          - '**/*.seed.sql'

strict:
    enabled: true
    stages:
        - dev
        - staging
        - prod

logging:
    enabled: true
    level: info
    file: .noorm/state/noorm.log
    maxSize: 10mb
    maxFiles: 5

BuildConfig

Controls which files are included in build operations.

FieldTypeDescription
includestring[]Glob patterns for included files (filter only)
excludestring[]Glob patterns for excluded files

Include acts as a filter, not an ordering mechanism. Files are executed in alphanumeric order—use numeric prefixes on directories and files to control the sequence. If not specified, all .sql files in the schema directory are included.

PathConfig (Settings)

Override default file locations. This is the only place paths are configured—configs do not carry their own.

FieldTypeDefaultDescription
sqlstring./sqlPath to SQL files
changesstring./changesPath to change directories

Stage

A stage is a config template that provides defaults and enforces constraints.

FieldTypeDescription
descriptionstring?Human-readable description
lockedboolean?When true, linked configs cannot be deleted
defaultsStageDefaults?Default values for new configs
secretsStageSecret[]Required secrets for completeness

StageDefaults

Initial values when creating a config from a stage.

FieldTypeDescription
dialectenum?Default database dialect
hoststring?Default hostname
portnumber?Default port
databasestring?Default database name
userstring?Default username
passwordstring?Default password
sslboolean?Default SSL setting
isTestboolean?Default test flag
protectedboolean?true becomes an access ceiling at resolution: resolved access is clamped to at most { user: 'operator', agent: 'viewer' } — a stricter config-level access survives unchanged

StageSecret

Defines a required secret for configs linked to a stage.

FieldTypeDefaultDescription
keystringRequiredSecret identifier
typeenumstringstring, password, api_key, or connection_string
descriptionstring?Human-readable description
requiredbooleantrueWhether the secret must be set for completeness

Rule

Conditional file inclusion/exclusion based on config properties.

FieldTypeDescription
descriptionstring?Human-readable label (e.g. "test seeds")
matchRuleMatchConditions that trigger this rule
includestring[]?Additional glob patterns to include
excludestring[]?Additional glob patterns to exclude

RuleMatch

Conditions for rule evaluation.

FieldTypeDescription
namestring?Match config by name
protectedboolean?Matches guarded(config)true if access.user !== 'admin' — despite the field's legacy name, this reads current access, not a stored flag
isTestboolean?Match by test flag
typeenum?Match by local or remote

All specified conditions must match for the rule to apply.

StrictConfig

Enforce stage usage.

FieldTypeDescription
enabledboolean?Enable strict mode
stagesstring[]?Required stages (configs must link to one)

LoggingConfig

File logging configuration.

FieldTypeDefaultDescription
enabledbooleantrueEnable file logging
levelenuminfosilent, error, warn, info, or verbose
filestring.noorm/state/noorm.logLog file path
maxSizestring10mbMaximum file size before rotation
maxFilesnumber5Maximum rotated files to keep

TeardownConfig

Controls database reset and teardown behavior. See Teardown.

FieldTypeDescription
preserveTablesstring[]?Tables always preserved during truncate operations
postScriptstring?SQL script run after schema teardown (relative to project root)

Universal Secrets

Top-level secrets: StageSecret[] on the settings file declares secrets required by all stages, using the same StageSecret shape.


Identity

Identity (Audit)

Simple identity used for tracking who executed database operations.

FieldTypeDescription
namestringDisplay name
emailstring?Email address
sourceenumHow identity was resolved

Identity sources (in resolution order):

PrioritySourceDescription
1configOverride specified in config (for bots/services)
2stateFrom encrypted state file (crypto identity)
3envNOORM_IDENTITY env var (CI pipelines)
4gitFrom git user.name and user.email
5systemFrom OS username

The resolver tries each source until it finds a valid identity.

CryptoIdentity

Full cryptographic identity for secure config sharing. Stored in encrypted state.

FieldTypeDescription
identityHashstringSHA-256 of canonical identity string
namestringDisplay name
emailstringEmail address
publicKeystringX25519 public key (hex)
machinestringMachine hostname
osstringOS platform and version
createdAtstringISO 8601 timestamp

Identity hash calculation:

SHA256(email + '\0' + name + '\0' + machine + '\0' + os)

The same user on different machines has different identities with different keypairs.

KnownUser

Cached identity discovered from database sync. Enables secure config sharing with team members.

FieldTypeDescription
identityHashstringSHA-256 of canonical identity string
emailstringUser email
namestringDisplay name
publicKeystringX25519 public key (hex)
machinestringMachine hostname
osstringOS platform and version
lastSeenstringISO 8601 timestamp of last activity
sourcestringConfig name where discovered

Key Files

Cryptographic keys are stored outside the project directory.

~/.noorm/
├── identity.key        # X25519 private key (hex, mode 600)
├── identity.pub        # X25519 public key (hex, mode 644)
└── identity.json       # CryptoIdentity metadata (name, email, machine, os, hash)

The private key never leaves the user's machine. The public key is shared via database identity tables. The permission check on identity.key is a threat-model check, not strict equality—it passes when no group or other bits are set, so 0400 is accepted too.


Encrypted Sharing

SharedConfigPayload

Format for encrypted config export files (*.noorm.enc).

FieldTypeDescription
versionnumberPayload format version
senderstringSender's email
recipientstringRecipient's email
ephemeralPubKeystringEphemeral X25519 public key (hex)
ivstringInitialization vector (hex)
authTagstringAuthentication tag (hex)
ciphertextstringEncrypted config (hex)

Exported Config Payload

The decrypted ciphertext is JSON of the shape { config, secrets }. There is no named type for it—it is built inline in src/tui/screens/config/ConfigExportScreen.tsx.

FieldTypeDescription
config.namestringConfig name
config.typeenumlocal or remote
config.isTestbooleanTest database flag
config.accessConfigAccessPer-channel access roles
config.protectedbooleanCompatibility echo of guarded(config), so an older importer still makes a safe (if coarser) access decision
config.connectionobjectdialect, host, port, database, ssl only
secretsMapConfig-scoped secrets

Note: user and password are intentionally omitted. Recipients provide their own credentials on import. pool and file system paths are not exported either.


Database Tables

noorm creates six tracking tables in the target database, all in schema migration v1.

Their names depend on the dialect. PostgreSQL and SQL Server get a dedicated noorm schema with clean names (noorm.version, noorm.change, and so on). MySQL and SQLite have no schemas, so they keep the __noorm_*__ prefixed forms in the default schema, which is what the headings below use.

Two column types are also dialect-dependent, and the tables below name the PostgreSQL form:

Doc typepostgresmssqlmysql / sqlite
serial (PK)serialint identity(1,1)integer + autoIncrement()
timestamptimestampdatetime2timestamp

Every timestamp type here is naive—it stores a wall clock with no offset. Code writing these columns must serialize UTC and parse back as UTC, or two clients in different timezones will disagree about what an instant means. The postgres and mysql drivers bind and parse a JS Date using the client's local offset, so passing a Date straight through is what breaks; see formatDateForDialect/parseDateFromDialect in src/core/lock/manager.ts.

__noorm_version__

Tracks noorm CLI version for internal schema migrations.

ColumnTypeConstraintsDescription
idserialPKPrimary key
cli_versionvarchar(50)NOT NULLnoorm version (semver)
noorm_versionintegerNOT NULLTracking table schema version
state_versionintegerNOT NULLState file format version
settings_versionintegerNOT NULLSettings file format version
installed_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPFirst installation
upgraded_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPLast upgrade

This table tracks noorm's internal schema, not the user's database schema. It is append-only: every migration and version-record update inserts a new row, so installed_at comes from the first row and everything else from the latest.

__noorm_change__

Tracks all operation batches—changes, builds, and ad-hoc runs.

ColumnTypeConstraintsDescription
idserialPKPrimary key
namevarchar(255)NOT NULLOperation identifier
change_typevarchar(50)NOT NULLbuild, run, or change
directionvarchar(50)NOT NULLchange or revert (see note below)
checksumvarchar(64)NOT NULL, DEFAULT ''SHA-256 of sorted file checksums
executed_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPWhen executed
executed_byvarchar(255)NOT NULL, DEFAULT ''Identity string
config_namevarchar(255)NOT NULL, DEFAULT ''Which config was used
cli_versionvarchar(50)NOT NULL, DEFAULT ''noorm version
statusvarchar(50)NOT NULLpending, success, failed, reverted, stale
error_messagevarchar(2000)NOT NULL, DEFAULT ''Error details (empty = no error)
duration_msintegerNOT NULL, DEFAULT 0Execution time (0 = never ran)

Index: idx_change_name_config on (name, config_name).

Two Direction types exist. The runner's in-memory Direction is 'commit' | 'revert'; the column's is 'change' | 'revert'. Tracker maps commitchange on write, so the database only ever holds change or revert. Query on those.

Name formats by change type:

Change TypeFormatExample
changeFolder name2024-01-15-add-users
buildbuild:{ISO timestamp}build:2024-01-15T10:30:00.000Z
runrun:{ISO timestamp}run:2024-01-15T10:30:00.000Z

__noorm_executions__

Tracks individual file executions within an operation.

ColumnTypeConstraintsDescription
idserialPKPrimary key
change_idintegerFK → change(id) ON DELETE CASCADE, NOT NULLParent operation
filepathvarchar(500)NOT NULLExecuted file path
file_typevarchar(10)NOT NULLsql or txt
checksumvarchar(64)NOT NULL, DEFAULT ''SHA-256 of file contents
cli_versionvarchar(50)NOT NULL, DEFAULT ''noorm version
statusvarchar(50)NOT NULLpending, success, failed, skipped
error_messagevarchar(2000)NOT NULL, DEFAULT ''Error details (empty = no error)
skip_reasonvarchar(100)NOT NULL, DEFAULT ''Why skipped (empty = not skipped); truncated to 100 chars on write
duration_msintegerNOT NULL, DEFAULT 0Execution time (0 = never ran)

Index: idx_executions_change_id on (change_id).

__noorm_lock__

Prevents concurrent operations on the same database.

ColumnTypeConstraintsDescription
idserialPKPrimary key
config_namevarchar(255)UNIQUE, NOT NULLLock scope
locked_byvarchar(255)NOT NULLIdentity of holder
locked_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPWhen acquired
expires_attimestampNOT NULLAuto-expiry time
reasonvarchar(255)NOT NULL, DEFAULT ''Lock reason (empty = none)

Locks automatically expire to prevent deadlocks from crashed processes.

__noorm_identities__

Stores user identities for team discovery.

ColumnTypeConstraintsDescription
idserialPKPrimary key
identity_hashvarchar(64)UNIQUE, NOT NULLSHA-256 of identity
emailvarchar(255)NOT NULLUser email
namevarchar(255)NOT NULLDisplay name
machinevarchar(255)NOT NULLMachine hostname
osvarchar(255)NOT NULLOS platform and version
public_keytextNOT NULLX25519 public key (hex)
encrypted_vault_keytextNULLVault key encrypted with this user's public key. NULL for users without vault access
registered_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPFirst registration
last_seen_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPLast activity

Auto-populated on first database connection when cryptographic identity is configured. encrypted_vault_key is the only nullable column in any tracking table.

__noorm_vault__

Team-shared secrets, encrypted with a vault key distributed via each member's public key. Full lifecycle in Vault.

ColumnTypeConstraintsDescription
idserialPKPrimary key
secret_keyvarchar(255)UNIQUE, NOT NULLSecret key name (e.g. API_KEY)
encrypted_valuetextNOT NULLAES-256-GCM encrypted value, JSON {iv, authTag, ciphertext}
set_byvarchar(255)NOT NULLIdentity that set this secret
created_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPWhen created
updated_attimestampNOT NULL, DEFAULT CURRENT_TIMESTAMPWhen last updated

Index: idx_vault_secret_key on (secret_key).


File System Structures

Change Directory

Changes live on disk as directories with a specific structure.

changes/
└── 2024-01-15-add-email-verification/
    ├── change/
    │   ├── 001_add-column.sql
    │   ├── 002_update-data.sql
    │   └── 003_files.txt
    ├── revert/
    │   ├── 001_drop-column.sql
    │   └── 002_restore-data.sql
    └── changelog.md

Change (Parsed)

When read from disk, changes are parsed into:

FieldTypeDescription
namestringFolder name (e.g., 2024-01-15-add-email-verification)
pathstringAbsolute path to directory
dateDate | nullParsed from name prefix (YYYY-MM-DD), null if no date prefix
descriptionstringHuman-readable, derived from name
changeFilesChangeFile[]Files in change/ subdirectory
revertFilesChangeFile[]Files in revert/ subdirectory
hasChangelogbooleanWhether changelog.md exists

ChangeFile

Individual file within a change.

FieldTypeDescription
filenamestringFile name (e.g., 001_alter-users.sql)
pathstringAbsolute path
typeenumsql or txt
resolvedPathsstring[]?For .txt files, paths to referenced files
statusenum?Runtime status after execution
skipReasonstring?Why file was skipped

File types:

TypeExtensionPurpose
sql.sql, .sql.tmplDirect SQL execution (with optional templating)
txt.txtManifest file listing paths to execute

Change Naming

Change folder names follow a convention:

{date}-{description}
ComponentFormatExample
dateYYYY-MM-DD2024-01-15
descriptionkebab-case (slugified)add-email-verification

The date prefix ensures chronological ordering. The description provides context. The separator is a hyphen, matching the date's own separators—the parser's prefix regex is /^(\d{4}-\d{2}-\d{2})-(.+)$/.

Files inside change/ and revert/ use a different convention: {sequence}_{slug}.{ext}, with an underscore (e.g. 001_add-column.sql).


Runtime Types

Operation Status

Used in __noorm_change__ and change results.

StatusMeaning
pendingNot yet executed
successCompleted successfully
failedExecution failed
revertedWas applied, then rolled back
staleSchema objects were torn down; needs re-run

Execution Status

Used in __noorm_executions__ and file results.

StatusMeaning
pendingNot yet executed
successCompleted successfully
failedExecution failed
skippedSkipped (see skip reason)

Skip Reasons

Free-form text, not an enum. The values emitted today:

ReasonMeaning
unchangedFile checksum matches previous run
already-runFile was already executed successfully
already appliedThe change as a whole was already applied
change failedParent change failed with no single culprit file
{file} failed: {error}Parent change failed at a named file; remaining files skipped

Lock

Active lock state returned by lock operations.

FieldTypeDescription
lockedBystringIdentity of holder
lockedAtDateWhen acquired
expiresAtDateAuto-expiry time
reasonstring?Why lock was acquired

Lock Options

Options for lock acquisition.

FieldTypeDefaultDescription
dialectenumpostgresDialect, used to pick the date format written to locked_at/expires_at
timeoutnumber300,000 (5 min)Lock duration in ms
waitbooleanfalseBlock until available
waitTimeoutnumber30,000 (30 sec)Maximum wait time in ms
pollIntervalnumber1,000 (1 sec)Check interval in ms
reasonstring?Lock reason

Run Options

Options for file execution.

FieldTypeDefaultDescription
forcebooleanfalseRe-run even if unchanged
concurrencynumber1Parallel file execution
abortOnErrorbooleantrueStop on first failure
dryRunbooleanfalseReport what would run without executing
previewbooleanfalseOutput rendered SQL without executing
outputstring | nullnullWrite preview output to file instead of stdout

Note: Concurrency defaults to 1 (sequential) because DDL operations often cannot run in parallel.


Template Context

Template Context Object ($)

Available in .sql.tmpl templates via Eta.

PropertyTypeDescription
$.<filename>anyAuto-loaded data from co-located files (key is the camelCased filename)
$.configobject?Active config values. Present only when no config.* data file exists in the template directory—a co-located file wins
$.secretsMapConfig-scoped secrets
$.globalSecretsMapApp-level secrets
$.envMapEnvironment variables

Built-in Helpers

HelperSignatureDescription
$.include(path)string → Promise<string>Include another SQL file
$.escape(value)string → stringSQL-escape a string
$.quote(value)any → stringEscape and quote a value
$.json(value)any → stringJSON stringify
$.now()() → stringCurrent ISO timestamp
$.uuid()() → stringGenerate UUID v4

Data File Auto-Loading

Files co-located with templates are automatically loaded.

ExtensionLoaderResult
.json, .json5JSON5 parserObject
.yaml, .ymlYAML parserObject
.csvCSV parserArray of objects
.js, .mjs, .tsDynamic importDefault export
.sqlFile readString
.dt, .dtzDT deserializerArray of rows

.dtzx is deliberately absent—there is no way to supply its passphrase from template context. The .js/.mjs/.ts loaders execute the file; the rest only parse.

Data files are available on $ by filename without extension:

sql/
├── users.sql.tmpl      # Template
├── users.json          # Available as $.users
└── seed-data.csv       # Available as $.seedData

Version Management

Version Layers

noorm tracks versions across three layers:

LayerStoragePurpose
schemaDatabase tableTracking table structure
stateState fileEncrypted state format
settingsSettings fileSettings YAML format

Each layer has independent migrations that run automatically when version mismatches are detected.

Current Versions

LayerCurrent Version
schema2
state3
settings1

Source of truth: CURRENT_VERSIONS in src/core/version/types.ts. See Version.


Lifecycle States

Application States

StateMeaning
idleNot started
startingInitialization in progress
runningNormal operation
shutting_downGraceful shutdown in progress
stoppedClean shutdown complete
failedError during startup or shutdown

Shutdown Phases

Shutdown proceeds through ordered phases:

PhaseOrderPurpose
stopping1Stop accepting new operations
completing2Wait for in-flight operations
releasing3Release database locks
flushing4Flush logger buffers
exiting5Final cleanup

Default Timeouts

PhaseDefault
Operations30 seconds
Locks5 seconds
Connections10 seconds
Logger10 seconds

Database Exploration

The explore module provides schema introspection across dialects.

ExploreCategory

Object types that can be explored:

CategoryDescription
tablesDatabase tables
viewsViews and materialized views
proceduresStored procedures
functionsUser-defined functions
typesCustom types, enums, domains
indexesTable indexes
foreignKeysForeign key constraints
triggersTable triggers
locksActive database locks
connectionsActive sessions

ExploreOverview

Count of objects in each category, returned by getOverview().

FieldTypeDescription
tablesnumberTable count
viewsnumberView count
proceduresnumberStored procedure count
functionsnumberFunction count
typesnumberCustom type count
indexesnumberIndex count
foreignKeysnumberForeign key count
triggersnumberTrigger count
locksnumberActive lock count
connectionsnumberActive connection count

Summary Types

Brief metadata for list views.

TableSummary:

FieldTypeDescription
namestringTable name
schemastring?Schema/database name
columnCountnumberNumber of columns
rowCountEstimatenumber?Estimated row count

ViewSummary:

FieldTypeDescription
namestringView name
schemastring?Schema/database name
columnCountnumberNumber of columns
isUpdatablebooleanWhether view is updatable

IndexSummary:

FieldTypeDescription
namestringIndex name
tableNamestringParent table
columnsstring[]Indexed columns
isUniquebooleanUnique constraint
isPrimarybooleanPrimary key index

ForeignKeySummary:

FieldTypeDescription
namestringConstraint name
tableNamestringSource table
columnsstring[]Source columns
referencedTablestringTarget table
referencedColumnsstring[]Target columns
onDeletestring?Delete action
onUpdatestring?Update action

Detail Types

Full metadata for detail views.

ColumnDetail:

FieldTypeDescription
namestringColumn name
dataTypestringSQL data type
isNullablebooleanAllows NULL
defaultValuestring?Default expression
isPrimaryKeybooleanPart of primary key
ordinalPositionnumberColumn order

TableDetail:

FieldTypeDescription
namestringTable name
schemastring?Schema name
columnsColumnDetail[]All columns
indexesIndexSummary[]Associated indexes
foreignKeysForeignKeySummary[]Outgoing foreign keys
rowCountEstimatenumber?Estimated rows

SQL Terminal

The sql-terminal module provides ad-hoc SQL execution with history tracking.

SqlHistoryEntry

A single query execution record.

FieldTypeDescription
idstringUUID v4 identifier
querystringSQL query executed
executedAtDateExecution timestamp
durationMsnumberExecution duration in ms
successbooleanWhether execution succeeded
errorMessagestring?Error details if failed
rowCountnumber?Rows returned or affected
resultsFilestring?Path to gzipped results

SqlExecutionResult

Full result from query execution.

FieldTypeDescription
successbooleanExecution status
errorMessagestring?Error if failed
columnsstring[]?Column names from result set
rowsobject[]?Row data as key-value objects
rowsAffectednumber?Rows affected (INSERT/UPDATE/DELETE)
durationMsnumberExecution time in ms

SqlHistoryFile

Persistent history stored at .noorm/state/history/{configName}.json.

FieldTypeDescription
versionstringSchema version
entriesSqlHistoryEntry[]History entries (newest first)

ClearResult

Result of clearing history.

FieldTypeDescription
entriesRemovednumberHistory entries deleted
filesRemovednumberResult files deleted

Summary

noorm's data model spans three tiers with clear separation of concerns:

  1. Encrypted State - Secrets, credentials, configs (.noorm/state/state.enc)
  2. Settings - Team rules, stages, build config (.noorm/settings.yml)
  3. Database Tables - Execution history, locks, identities, vault (noorm.* on postgres/mssql, __noorm_*__ on mysql/sqlite)

The change file system provides versioned changes, while runtime types enable flexible execution modes (dry run, preview, force).

All types follow consistent patterns:

  • Clear status enums for operation tracking
  • Duration timing on all executions
  • Error messages alongside status
  • Checksum-based change detection