Skip to content

Function Registration History

Every function update, status change, rollback, and deletion is recorded as an immutable entity event on the ironflow:fn:{id} stream. This gives you a full audit trail of configuration changes with per-version snapshots, actor attribution, and change reasons.

Client
│ RegisterFunction(id, config, change_reason)
Handler.RegisterFunction
├─[new]──► store.CreateFunction(fn, snapshot) ← single transaction
│ ├─ INSERT INTO functions (version=1)
│ └─ INSERT INTO events
│ entity_id = "ironflow:fn:{id}"
│ entity_type = "function"
│ entity_version = 1
│ name = "FunctionCreated"
│ data = { snapshot of initial config, change_type = "created" }
└─[update]─► snapshot post-update config (state AT new version)
store.UpdateFunction(fn, snapshot) ← single transaction
├─ SELECT MAX(entity_version)
│ FROM events
│ WHERE entity_id = "ironflow:fn:{id}"
├─ INSERT INTO events
│ entity_id = "ironflow:fn:{id}"
│ entity_type = "function"
│ entity_version = prev + 1
│ name = "FunctionUpdated"
│ data = { snapshot of new config }
└─ UPDATE functions SET ... WHERE id = ?
engine.NotifyFunctionUpdated(ctx, fn)
engine.SyncFunction(ctx, fn)

Same transactional path as a config update, with change_type = "status_change".

DeleteFunction(id)
├─ 1. GetFunction(id) ← capture final state
├─ 2. store.UpdateFunction(fn, snapshot)
│ name = "FunctionDeleted"
│ change_type = "delete"
│ └─► entity event persists after row is gone
└─ 3. store.DeleteFunction(id) ← row deleted, event survives

The deletion snapshot is written before the row is removed so the final configuration remains queryable through ListFunctionHistory even after the function no longer exists.


ListFunctionHistory(fn_id, limit, from_version)
store.ListEntityEvents(
entity_id = "ironflow:fn:{fn_id}",
direction = "backward", ← newest first
BeforeVersion = from_version, ← exclusive backward cursor (proto field `from_version` maps here)
limit = limit + 1 ← +1 to detect has_more
)
events table rows
└─► for each row: json.Unmarshal(data) → FunctionConfigSnapshot
[]FunctionHistoryEntry
{ entity_version, snapshot, actor_id, change_reason, change_type }
has_more = len(results) > limit

Entries are returned newest-first. The proto field from_version maps to opts.BeforeVersion (an exclusive backward cursor). Pass the lowest entity_version from the previous page to fetch the next.

GetFunctionAtVersion(fn_id, version)
store.ListEntityEvents(
entity_id = "ironflow:fn:{fn_id}",
FromVersion = version, ← point lookup at this exact version
limit = 1,
direction = "forward"
)
├─[not found or version mismatch]──► CodeNotFound
└─[found]──► FunctionHistoryEntry

RollbackFunction(fn_id, target_version, change_reason)
├─ 1. GetFunction(fn_id)
│ currentFn ← current live config
├─ 2. GetFunctionAtVersion(target_version)
│ snapshot.Function ← old config to restore
│ └─ guard: reject if snapshot.Status == archived
├─ 3. Reconstruct fn from snapshot
│ fn.ID = currentFn.ID
│ fn.Version = currentFn.Version + 1
│ fn.EnvironmentID = currentFn.EnvironmentID
├─ 4. Build rollback snapshot (state AT new version = restored config)
│ {
│ Function: fn, ← restored config (state at new version)
│ ChangeType: "rollback",
│ ChangeReason: "rollback to v{N}"
│ }
├─ 5. store.UpdateFunctionVersioned(fn, currentFn.Version, snapshot)
│ CAS: WHERE id = ? AND version = ?
│ ├─[version conflict]──► CodeAborted (retry)
│ └─[ok]──► same atomic transaction as write flow
│ entity event written, function row updated
├─ 6. engine.SyncFunction(ctx, fn)
└─ 7. engine.NotifyFunctionUpdated(ctx, fn)

Rollback is itself recorded as a new history entry, so the audit trail shows both the intent and the resulting config change.


History events are rows in the shared events table, namespaced under the reserved ironflow: entity ID prefix. User-defined entity streams cannot use this prefix.

events table
┌──────────────────┬──────────────┬────────────────┬──────────────────────┐
│ entity_id │ entity_type │ entity_version │ name │
├──────────────────┼──────────────┼────────────────┼──────────────────────┤
│ ironflow:fn:abc │ function │ 1 │ FunctionCreated │
│ ironflow:fn:abc │ function │ 2 │ FunctionUpdated │
│ ironflow:fn:abc │ function │ 3 │ FunctionDeleted │
│ user:order:xyz │ order │ 1 │ OrderPlaced │ ← user stream
└──────────────────┴──────────────┴────────────────┴──────────────────────┘

Each data column is a FunctionConfigSnapshot JSON blob containing:

FieldDescription
functionFull function config as of this version (state AT this entity_version, not before it)
actor_idAPI key or user that triggered the change
change_reasonOptional free-text label (e.g. "deploy v2.1.0")
change_typecreated | update | status_change | rollback | delete
snapshot_version0 (absent) = legacy pre-change semantics; 1 = current post-change semantics

RPCDescription
RegisterFunctionPass change_reason to annotate the history entry
ListFunctionHistoryPaginated history, newest-first, keyset via from_version (maps to BeforeVersion). limit defaults to 50, capped at 200.
GetFunctionAtVersionFetch snapshot at a specific entity_version
RollbackFunctionRestore a prior version; recorded as a rollback entry