Glossary
Single source of truth for all terms used across Hegemony — code, UI, API, documentation, and Temporal handlers.
Table of Contents
- Flow Engine
- Status Enumerations
- Artifacts & Events
- Devices & Infrastructure
- Secrets & Credentials
- Schedules
- Notifications
- Webhooks
- Authentication & Authorization
- Architecture Concepts
- Configuration Exchange
- Git Integration
- Acronym Quick-Reference
Flow Engine
Flow / Flow Definition
A Flow (stored as FlowDefinition in the database) is a declarative, version-controlled workflow expressed as a directed graph of nodes and edges. Flows are authored in JSON/YAML and executed by the Worker via Temporal.
- Code:
packages/core/·apps/api/models.py(FlowDefinition) - Schema:
apps/api/schemas.py(FlowGraphDefinition) - Docs: architecture/overview.md — Flow Engine
Run / Flow Run
A Run is a single execution instance of a Flow. It tracks overall status, resolved targets, resolved inputs, and the Temporal workflow_id used to identify the Temporal workflow that is executing it.
- Code:
apps/api/models.py(Run) ·packages/core/enums.py(RunStatus) - API:
POST /flows/{id}/runs·GET /runs/{id} - Docs: architecture/overview.md — Database Schema
Run Trigger Source
What started a Run — one of five values. Recorded on the run's audit entry as its trigger_source detail, and what Manual Run Control is evaluated against.
| Value | Started by |
|---|---|
manual | A person via the UI Run button or POST /runs |
run_now | A person pressing Run now on a Schedule — still the schedule's trigger, so deliberately distinct from manual |
schedule | The Scheduler Service, on the schedule's cadence |
webhook | An inbound call to a Webhook Endpoint |
flow | A parent flow's flow.run step, creating a nested child run |
- Code:
packages/core/enums.py(RunTriggerSource) - Docs: features/manual-run-control.md
Manual Run Control
Whether a Flow accepts runs a person starts by hand. Turned off, the flow is automation-only: a Schedule, a Webhook Endpoint and a parent flow's flow.run step all still start it, while POST /runs returns 409. It is the on/off switch the Manual trigger card gained so that all four triggers have one.
Stored on the flow rather than in its definition, so it takes effect without a commit and applies to every version — a manual run pinned to an older committed version is refused too. An admin may override one run with POST /runs?force=true, which is recorded in the Audit Log.
- Code:
apps/api/models.py(FlowDefinition.manual_runs_enabled) ·apps/api/services/manual_run_policy.py - API:
PATCH /flows/{flow_id}/manual-runs - Docs: features/manual-run-control.md · guides/flows.md — How a flow gets started
Step
A Step is a declarative unit of work within a Flow. It is defined inside a flow's graph as a Node of type step, and specifies a Handler, optional parameters, target device role mappings, and any Tags the author writes — conventionally a phase and a kind among them.
Steps are defined once in the flow graph definition. At runtime they are instantiated as Step Runs.
Step Run
A Step Run is the runtime instance of a Step within a specific Run. Step runs are created dynamically by the worker during execution (not pre-created). They track per-execution state such as status, timestamps, attempt count, error messages, and result JSON.
- Code:
apps/api/models.py(StepRun) ·packages/core/enums.py(StepStatus) - Docs: architecture/overview.md — Database Schema
Node
A Node is a vertex in a flow's execution graph. Every node has a unique id, a type, and a human-readable name. The following node types exist:
| Type | Description |
|---|---|
start | Entry point of the graph. Every flow has exactly one. |
step | Executable unit; runs a handler against target devices. |
approval | Pauses execution until a user approves or rejects. |
fork | Splits execution into parallel branches. |
join | Waits for parallel branches to complete. |
terminal | Terminal (end) node; transitions the run to its final status. |
group | (Reserved) Visual grouping of nodes; no runtime semantics. |
- Code:
packages/core/enums.py(FlowGraphNodeType) - Schema:
apps/api/schemas.py(FlowGraphNode)
Edge
An Edge connects two Nodes in a flow graph. Each edge has a source node ID, a source_outcome (e.g., success, failure, fork), and a target node ID. Edges carry no id of their own — they are identified by the (source, source_outcome, target, type) combination, which must be unique within a graph. Edges with type: termination are used to signal Background Monitor termination and do not route tokens.
- Code:
packages/core/enums.py(EdgeType,FlowGraphOutcome) - Schema:
apps/api/schemas.py(FlowGraphEdge) - Docs: architecture/overview.md — Flow Definition Structure
Handler
A Handler is a Python class that implements the execution logic for a Step. Handlers are registered by namespaced id (e.g., probe.connectivity, netcli.execute) and resolved at runtime by the Handler Registry. Each handler implements execute(ctx) → HandlerResult. Handlers ship in hegemony-steps-* plugin wheels (repo hegemony-step-plugins) built on the hegemony-step-sdk package; every handler, monitor.* included, ships as a wheel.
- Code: base class
hegemony_step_sdk.BaseHandler(plugin wheels); the monitor runtime (sampling loops, not the handlers) is in-tree inapps/worker/monitor_handlers.py - Context:
HandlerContext— providesrun_id,step_run_id, device info, config/params,services(host ABI) - Result:
HandlerResult—success,summary,error,evidence - Docs: architecture/overview.md — Handler Registry Pattern
Handler Registry
The Handler Registry is the dictionary of all registered Handlers. Plugin wheels register handler classes through the hegemony.step_handlers entry-point group (one claimed namespace prefix per wheel); workers look up handlers by their string handler_id (e.g., probe.connectivity) at execution time.
- Code:
packages/core/step_handlers/registry.py
Step Tag
A Step Tag is a key=value pair a flow author writes on a Step, in the same shape as device, site and flow tags. Tags group and annotate steps for the people reading the graph, the run timeline and the evidence list. They do not steer execution: ordering, scheduling and handler selection never consult them. A step's own config may reference one as {{ tags.env }}, which resolves into that step's input like any other template variable. They are free-typed — no vocabulary is registered — and the editor suggests the keys the organization already uses.
A step run snapshots the tags the step carried when it ran, so editing the flow afterwards never rewrites a past run's evidence.
- Code:
packages/core/step_tags.py(the contract) ·apps/api/schemas.py(StepTags) ·apps/api/models.py(StepRun.tags_json) - API:
GET /flows/step-tag-keys(keys already in use) - Docs: guides/flows.md — Tags
Conventional keys
Two keys are conventional rather than reserved: phase and kind. The editor suggests them first, with the values below, and a step reads them like any other tag, as {{ tags.phase }} / {{ tags.kind }} — there is no shorter form. They are entries of the step's tags map and nothing else: a step node has no phase or kind field, and the API rejects a definition that writes one (flow files and bundles from earlier versions, which did, are upgraded on import — see Git Integration). Any value is accepted under either key and nothing enforces them — they are ordinary tags with a shared habit behind them. A step saved without a phase tag records no phase and shows no phase chip.
phase groups steps for display and audit; ordering is determined by the graph edges, never by the phase:
| Phase | Typical use |
|---|---|
PREPARE | Pre-flight checks, evidence collection, backups |
IMPLEMENTATION | Changes applied to devices |
VERIFY | Post-change validation |
CLEANUP | Housekeeping, temporary file removal |
kind names the behaviour category of a step — the Handler still decides what the step does. Handlers declare the kinds they support (supported_kinds, the SDK's StepKind enum), which the installed-handlers page lists as their capabilities; that enum describes handlers, not the tag, and the step editor does not check one against the other:
| Kind | Description |
|---|---|
CHECK | Read-only validation or evidence collection |
ACTION | Mutating operation on a device |
WAIT | Pause until a condition is met or a timer expires |
TRANSFER | File/image transfer to/from a device |
EXECUTE | Generic code execution |
- Code:
packages/core/step_tags.py(DEFAULT_TAG_KEYS) ·packages/core/enums.py(StepKind, handler capability)
Graph Runtime
The Graph Runtime (GraphRuntime class in apps/worker/flow_graph_runtime.py) is the component inside the Temporal workflow that orchestrates token-based graph traversal. It manages all node types, spawns tokens for outgoing edges, and implements Fork/Join parallelism.
- Code:
apps/worker/flow_graph_runtime.py
Token (Execution Token)
An Execution Token is an internal unit of forward progress through the flow graph. When a node completes, it produces tokens for each of its outgoing edges. Multiple active tokens represent parallel branches of execution (see Fork Node).
Fork Node
A Fork Node is a node of type fork that simultaneously spawns tokens to all of its outgoing edges, creating parallel execution branches. All branches start immediately.
- See also: Join Node
Join Node
A Join Node is a node of type join that collects tokens arriving from parallel branches. Execution continues after the node once the configured Join Mode condition is satisfied.
- Code:
packages/core/enums.py(JoinMode,JoinFailurePolicy,JoinCancelPolicy)
Join Mode
Join Mode controls when a Join Node allows execution to continue:
| Mode | Behavior |
|---|---|
all | Wait for all incoming branches to complete (AND semantics) |
any | Continue when the first successful branch arrives (OR semantics); remaining branches may be cancelled |
- Code:
packages/core/enums.py(JoinMode)
Join Failure Policy
Join Failure Policy controls what a Join Node does when a branch fails:
| Policy | Behavior |
|---|---|
propagate | If any branch fails, the join fails after all branches complete |
fail_fast | Fail immediately when any branch fails |
ignore_failures | Continue if at least one branch succeeds (only meaningful with any mode) |
- Code:
packages/core/enums.py(JoinFailurePolicy)
Trigger Limit
Trigger Limit controls how many times a Step Node may be executed when it has multiple incoming edges and therefore can receive more than one execution token.
Approval Nodes are always fixed at 1 (first-arrival-wins) and do not support custom trigger_limit values.
| Value | Behavior |
|---|---|
1 (default) | First-arrival-wins — the first token triggers execution; all subsequent tokens are silently absorbed and do not re-execute the node or route successors |
0 | Unlimited — execute the node for every arriving token (no deduplication) |
| N (≥ 2) | Count-bounded — allow up to N executions; the (N+1)-th token is absorbed |
The default (1) is useful when parallel branches converge on a shared step and you want the step to run at most once regardless of how many branches reach it.
- Code:
apps/api/schemas.py(FlowGraphNode.trigger_limit),apps/worker/flow_graph_runtime.py(get_trigger_limit),apps/worker/graph_engine/engine.py(deduplication logic),apps/worker/graph_engine/state.py(GraphExecutionState.trigger_counts,trigger_outcomes)
Approval Node
An Approval Node is a node of type approval that pauses flow execution until an authorized user approves or rejects the request. On approval the run continues; on rejection or expiry the run fails.
- Code:
packages/core/enums.py(ApprovalStatus) - Docs: architecture/auth.md (separation of duties)
Terminal Node
A Terminal Node is a node of type terminal that marks the end of a flow graph. When a token reaches a terminal node the Run transitions to its final status (SUCCESS or FAILED).
Failure Policy
The Failure Policy controls what the Graph Runtime does when a step fails:
| Policy | Behavior |
|---|---|
FAIL_FAST | Halt all further execution immediately |
CONTINUE_ON_ERROR | Mark the node failed but continue remaining branches |
Background Monitor
A Background Monitor is a step with schedule_mode: until_join (or another MonitorScheduleMode). It runs asynchronously alongside the main execution branches without blocking them. The run's Join Node is responsible for stopping it when the join condition fires (via a termination edge).
All monitors are automatically stopped when the Run reaches a terminal state.
- Code:
packages/core/enums.py(MonitorScheduleMode)
Monitor Schedule Mode
Controls when a Background Monitor self-terminates:
| Mode | Behavior |
|---|---|
count | Run exactly N ticks, then stop and route a token to successors |
duration | Run for duration_s seconds, then stop and route a token |
until_join | Fire-and-forget; stopped by a termination edge from a Join Node |
- Code:
packages/core/enums.py(MonitorScheduleMode)
Status Enumerations
Run Status
The lifecycle state of a Run:
| Value | Description |
|---|---|
PENDING | Created, not yet started by Temporal |
RUNNING | Actively executing |
PAUSED | Manually paused |
WAITING_APPROVAL | Paused at an Approval Node |
SUCCESS | All branches completed successfully |
FAILED | At least one branch failed |
CANCELLED | Explicitly cancelled by a user |
- Code:
packages/core/enums.py(RunStatus)
Step Status
The lifecycle state of a Step Run:
| Value | Description |
|---|---|
PENDING | Queued, not yet started |
RUNNING | Handler executing |
SUCCESS | Handler returned successfully |
FAILED | Handler returned an error |
SKIPPED | Skipped due to branch cancellation or failure policy |
CANCELLED | Explicitly cancelled |
- Code:
packages/core/enums.py(StepStatus)
Approval Status
The lifecycle state of an approval request on an Approval Node:
| Value | Description |
|---|---|
PENDING | Waiting for a reviewer |
APPROVED | Approved; execution continues |
REJECTED | Rejected; run transitions to failed |
EXPIRED | Approval window timed out |
CANCELLED | Cancelled (e.g., run was cancelled) |
- Code:
packages/core/enums.py(ApprovalStatus)
Monitor Status
The lifecycle state of a Background Monitor instance:
pending → running → paused / stopped / completed / failed
| Value | Description |
|---|---|
pending | Scheduled but not yet started |
running | Actively collecting samples |
paused | Temporarily suspended (can be resumed) |
stopped | Explicitly stopped before the schedule end; no further samples collected |
completed | Schedule ended normally (count/duration exhausted or join node reached) |
failed | Terminated due to an unrecoverable error |
- Code:
packages/core/enums.py(MonitorStatus)
Artifacts & Events
Artifact
An Artifact is structured evidence captured by a Handler during Step execution. Artifacts are associated with a Step Run and stored in the run_artifacts table. They are used for compliance, debugging, and change verification.
- Code:
apps/api/models.py(RunArtifact)
Artifact Kind
The type/format of an Artifact:
| Kind | Description |
|---|---|
cli_output | Raw CLI command output |
facts_json | Structured device facts (e.g., version, inventory) |
transport_meta | SSH/transport session metadata |
step_result | Structured handler result |
poll_result | Result of a polling check |
connectivity_monitor | Output from connectivity monitoring |
comparison_result | Before/after diff |
- Code:
packages/core/enums.py(ArtifactKind)
Run Event
A Run Event is an immutable log entry associated with a Run and optionally a Step Run. Events are streamed to the UI via SSE and stored persistently in run_events.
- Code:
apps/api/models.py(RunEvent)
Event Kind
The type of a Run Event:
| Kind | Description |
|---|---|
stage_change | Status transition for a run or step |
log | Informational log message |
error | Error message |
notification | Notification-related event |
progress | Fine-grained progress update |
graph.fork_split | Fork node spawned parallel branches |
graph.join_arrival | A branch token arrived at a join node |
graph.join_complete | Join node fired and execution continues |
- Code:
packages/core/enums.py(EventKind)
Devices & Infrastructure
Device
A Device is a managed network device (router, switch, firewall, etc.) registered in Hegemony. Devices belong to Sites, carry platform and connectivity metadata, and reference Secrets for SSH credentials.
- Code:
apps/api/models.py(Device) - API:
/devices
Site
A Site is a logical location (data centre, branch office, etc.) that organises Devices into a hierarchy. Sites can be nested (parent/child relationships) to model geographic or administrative hierarchies.
- Code:
apps/api/models.py(Site) - API:
/sites
Platform
Platform identifies the operating-system family of a Device:
| Value | Description |
|---|---|
ios-xe | Cisco IOS XE |
- Code:
packages/core/enums.py(Platform)
Transport
Transport is the protocol used to communicate with a Device:
| Value | Description |
|---|---|
ssh | Secure Shell (default) |
netconf | NETCONF/XML management |
gnmi | gNMI streaming telemetry |
- Code:
packages/core/enums.py(Transport)
Target
A Target specifies which devices a Step should act upon. Targets can be specified as:
- Role-based (
type: role) — resolved from the Run's device-role mapping at runtime. - IP-based (
type: ip) — explicit IPv4/IPv6 address list.
Target Role
Target Role is a user-defined string naming the function a device plays within a multi-device operation. Flows define the role names they need (for example primary, hsrp_peer, blue_site, or precheck_peer), and runtime target selectors resolve devices by those custom role strings.
Secrets & Credentials
Secret
A Secret is sensitive data (password, API key, certificate, private key) managed by Hegemony. Only metadata (name, description, backend path) is stored in the application database; the value lives exclusively in a configured Secret Backend.
- Docs: features/secrets.md
Secret Reference
A Secret Reference is a Jinja template expression that identifies a secret at runtime without embedding its value. The preferred syntax uses {{ }} Jinja helpers:
| Type | Syntax |
|---|---|
| Dynamic backend (e.g. Vault) | {{ secret('vault://path/key') }} |
| Environment variable (platform configuration only) | {{ env('VAR_NAME') }} |
| File (Docker secret, platform configuration only) | {{ file('filename') }} |
Secret references are stored in flow definitions and device records. The Worker resolves them just-in-time before executing a handler. env() and file() read the platform's own environment, so only secret backend and inventory provider settings may use them.
Secret Backend
A Secret Backend is a pluggable storage provider for secret values. Supported backends include:
- Internal OpenBao (opt-in, auto-bootstrapped) — OpenBao KV v2, bundled with the compose stack and enabled by the
openbao-internaloverlay. - External OpenBao or Vault — A customer-managed server.
- Environment variables (
{{ env('VAR') }}) — Read from the API or worker process environment; platform configuration only, such as a backend's own bootstrap token.
Backend configuration is stored in the database and managed via Settings → Secret Backends in the UI.
Internal OpenBao
The Internal OpenBao is an OpenBao KV v2 instance — the Linux Foundation fork of HashiCorp Vault — bundled with the Hegemony compose stack and automatically bootstrapped on startup by the openbao-init container. It requires no manual configuration, but it is opt-in: it ships behind the openbao-internal compose overlay, and HEGEMONY_OPENBAO_INTERNAL_ENABLED is false by default in production. With the overlay applied it becomes the default Secret Backend only at bootstrap, and only if no other default is configured — it never displaces a default an admin has already chosen.
Secret references still use the vault:// scheme: the backend keeps its original tag and scheme so no stored reference has to change.
Schedules
Schedule
A Schedule automates Flow execution by triggering Runs at a specified time or cadence. Schedules store the target flow, version, input parameters, device role mappings, and timing configuration.
- Code:
apps/api/models.py(Schedule) - API:
/schedules - Docs: features/schedules.md · architecture/overview.md — Schedule Automation
Schedule Type
Controls whether a Schedule fires once or repeatedly:
| Type | Description |
|---|---|
one_time | Execute once at a specific date/time; transitions to COMPLETED afterward |
recurring | Execute repeatedly — by interval (every N seconds) or cron expression |
- Code:
packages/core/enums.py(ScheduleType)
Schedule Status
| Value | Description |
|---|---|
active | Will fire when due |
disabled | Paused by user; will not fire |
completed | One-time schedule that has already executed |
- Code:
packages/core/enums.py(ScheduleStatus)
Scheduler Service
The Scheduler Service (apps/scheduler/) is a standalone process (separate from the Worker) that polls the API for due schedules and triggers them. It runs every HEGEMONY_SCHEDULE_POLL_INTERVAL_SECONDS (default: 15 s).
- Code:
apps/scheduler/run.py·apps/scheduler/scheduler.py - Docs: architecture/overview.md — Scheduler Architecture
Notifications
Notification Destination
A Notification Destination is a configured delivery channel (e.g., an email address or a Shoutrrr URL) to which alert messages are sent. Destinations can be enabled or disabled independently of subscriptions.
- Code:
apps/api/models.py(NotificationDestination) - API:
/notifications/destinations - Docs: features/notifications.md
Notification Subscription
A Notification Subscription connects a Flow to a Notification Destination for a specific Notification Event. When the event fires the Worker dispatches an alert to all matching enabled destinations.
- Code:
apps/api/models.py(FlowNotificationSubscription) - API:
/notifications/subscriptions
Notification Event
The trigger type for a notification. Events are grouped into run-lifecycle and approval categories:
| Event | Trigger |
|---|---|
run.started | Run begins execution |
run.paused | Run is manually paused |
run.resumed | Run is resumed |
run.cancelled | Run is cancelled |
run.failed | Run transitions to FAILED |
run.completed | Run transitions to SUCCESS |
approval.requested | An approval node is reached |
approval.approved | Approval granted |
approval.rejected | Approval rejected |
approval.expired | Approval window timed out |
- Code:
packages/core/enums.py(NotificationEvent)
Shoutrrr
Shoutrrr is an open-source notification dispatch library that supports many services (Slack, Discord, Telegram, Teams, etc.) via a unified URL format. Hegemony uses it as one of the Notification Destination types (shoutrrr).
Webhooks
Webhook Endpoint
A Webhook Endpoint is an HTTP endpoint (POST /hooks/{path_token}) that lets external systems (e.g., CI/CD pipelines, monitoring tools) trigger Runs of one linked Flow. Each endpoint has a unique path token and an auth mode (HMAC-SHA256 or bearer token via a linked Secret), and targets exactly one flow via its webhook_endpoints.flow_id column (nullable FK, ON DELETE SET NULL — mirroring Schedule.flow_id).
- Code:
apps/api/models.py(WebhookEndpoint) - API:
/webhooks(management) ·POST /hooks/{path_token}(trigger) - Docs: features/webhooks.md
Webhook Endpoint Target
Removed concept. Earlier iterations linked webhook endpoints to flows through a webhook_endpoint_targets N:N association table (WebhookEndpointTarget model). Webhook endpoints now target exactly one flow via webhook_endpoints.flow_id; the association table and model were removed by migration 20260824_039_webhook_single_flow.py. A webhook that must start several flows targets one parent flow whose graph runs the children as nested child flows (flow.run steps).
Status: Removed (2026-08) — superseded by
webhook_endpoints.flow_id.
Authentication & Authorization
OIDC / Keycloak
Hegemony uses OpenID Connect (OIDC) with Keycloak as the identity provider. The React SPA authenticates via the Authorization Code + PKCE flow; the FastAPI backend validates the resulting JWT against Keycloak's JWKS endpoint.
- Docs: architecture/auth.md
JWT
A JSON Web Token is the bearer credential used by authenticated users. It is issued by Keycloak, signed with RS256, and validated by the API on every request.
PKCE
Proof Key for Code Exchange — an OAuth2 extension that prevents authorization code interception attacks for public clients (SPAs). The hegemony-ui Keycloak client uses PKCE.
RBAC
Role-Based Access Control — the authorization model used by Hegemony. Permissions are declared as actions in apps/api/auth/actions.yaml, every API route is mapped to exactly one action in apps/api/auth/routes.yaml, and RBACMiddleware enforces the result centrally. The four primary roles are:
| Role | Description |
|---|---|
admin | Full access including user management |
operator | Can create and execute flows, manage devices and schedules |
approver | Can approve/reject approval requests (cannot create runs) |
auditor | Read-only access for compliance and monitoring |
- Code:
apps/api/auth/actions.yaml·apps/api/auth/routes.yaml·apps/api/auth/permissions.py - Docs: architecture/auth.md — Role-Based Access Control
Action (RBAC)
An action is the unit of permission an administrator allocates to a policy: a resource:verb key such as flow:view or run:trigger, declared in apps/api/auth/actions.yaml with a default policy and a description. Every API route belongs to exactly one action (apps/api/auth/routes.yaml) and inherits its policy. Under Settings → Permissions the Actions tab reallocates a whole action to another policy (all of its routes follow); the Routes tab pins a single method and path, which wins over the action. The effective policy is resolved as route override → action override → action default; public, internal and custom policies are structural and never overridable. Besides the role policies, either layer can be set to disabled, which switches the action or route off for every caller until an administrator assigns another policy. No action may declare disabled as its default, and the actions the console needs to recover (permission:manage, auth:me) refuse it.
- Code:
apps/api/auth/actions.yaml·apps/api/auth/permissions.py - Docs: reference/rbac.md · architecture/auth.md — Actions and Overrides
Principal
A Principal is the authenticated identity of the caller of an API request. It carries sub (subject ID), username, email, realm roles, and client roles. In development mode (HEGEMONY_AUTH_DISABLED=true) a mock admin principal is returned.
- Code:
apps/api/auth/(Principaldataclass)
BFF Ticket
A BFF (Backend-for-Frontend) Ticket is a short-lived, server-side session token issued by POST /auth/bff/ticket and used as a query-parameter credential for SSE connections (browsers cannot send Authorization headers in EventSource).
Internal Token
The Internal Token (X-Internal-Token HTTP header) is a shared secret used by the Worker and Scheduler Service to call internal API endpoints (/internal/...). Configured via HEGEMONY_INTERNAL_API_TOKEN. In production this variable is required at startup.
Audit Log
The Audit Log is the append-only record of who changed what. One entry per state change, and one per refused request that names someone — a caller who lacked the role, or a token that still identifies its owner. A refusal with no identity behind it goes to the structured log instead, because it carries nobody to attribute and anyone can produce it at any rate. An entry names the actor and how they authenticated, the object, the action and outcome from a fixed vocabulary, the client address and request, and, for updates, a redacted snapshot of the object before and after with a field-level diff. Entries are written in the same transaction as the change they describe, so the two commit or roll back together; attempts lost to a rollback go to the structured log. Every mutating API route is either audited or exempt for a stated reason, checked by the test suite. Platform admins read it under Settings → Audit Log and on each object's page. See Audit Log.
Architecture Concepts
API (Control Plane)
The API is the FastAPI application (apps/api/) that serves as the control plane. It handles all user-facing REST requests, persists state to PostgreSQL, starts Temporal workflows, and streams real-time Run Events via SSE.
Worker
The Worker (apps/worker/) is the Temporal worker process that executes Handler activities. It polls the Temporal task queue, runs step handlers against network devices, and reports results back to the API via internal HTTP endpoints.
Temporal
Temporal is the open-source workflow orchestration platform used by Hegemony. It provides durable, fault-tolerant workflow execution, automatic retries, and a state management layer that survives process restarts.
SSE (Server-Sent Events)
Server-Sent Events is the HTTP/1.1 push mechanism used to stream Run Events from the API to the React UI in real time. The endpoint is GET /runs/{id}/events.
BFF Tickets are used to authenticate SSE connections because browsers cannot attach Authorization headers in the EventSource API.
File Repository
A File Repository is Hegemony's managed shared file store, backed by S3-compatible object storage. File repositories support logical folder hierarchies and file operations (upload, import, move, rename, delete) for artifacts such as firmware images, scripts, and supporting assets.
- Code:
apps/api/models.py(FileRepository,StoredFile,FileRepositoryFolder) - API:
/file-repositories - Docs: features/file-repositories.md
Registry Credential
A Registry Credential is an organization's login for an external container registry (a host such as ghcr.io or docker.io, a username, and the password as a secret reference). Container steps in the organization pull images from that host with it automatically: the worker resolves the reference for the step and removes it when the step ends. One credential per host per organization; the value never enters the database.
- Code:
apps/api/models.py(RegistryCredential),apps/worker/step_handlers/docker_config.py - API:
/registry-credentials - Docs: features/container-registries.md
Platform Registry
The Platform Registry is Hegemony's own container registry (zot on the S3 object store, started by the registry compose overlay). Container steps keep copies of the images they pull in it — their image cache, under orgs/<slug>/<upstream host>/… — and pull the copies on later runs; global/… holds platform-wide images. Steps reach it with short-lived tokens the worker mints per step; every request is authorized by the API through the registry proxy, and nobody logs in. See Container Registries.
Object Name
The human-facing name you give a flow, site, device, secret, secret backend, schedule, webhook, notification destination, repository, organization, API token or graph node. Every one of them follows the same rules:
- Surrounding whitespace is stripped, and a name that is blank after stripping is rejected. Stripping is not cosmetic: flow versioning picks the next version by exact name match, so
Nightly Backupand the same name with a trailing space would otherwise become two flows with separate version histories. - At most 128 characters.
- No single word longer than 64 characters. Names appear in table cells, node boxes and page headers, none of which can break inside an unbroken run of characters, so one long pasted token would overflow whatever it lands in.
- No control characters — line breaks and tabs break table rows, YAML export and log lines.
Names need not be unique, and they are not identifiers: variable names, run-variable names and organization slugs are separate, stricter things with their own patterns.
Names that predate these limits still load; they are rejected only when something tries to save them again.
The rules are enforced on the API's create and update requests, on bulk import through Configuration Exchange, and mirrored in the UI so a bad name is caught before the round trip. An import that carries a bad name fails with the offending section and entry named, rather than being silently corrected. References that address an entry by name are stripped the same way as the name itself — a site's parent path, for one — so stripping cannot leave a reference pointing at nothing.
Import checks the name rather than the bundle schema declaring it, because the bundle models serialize live rows on export too: a name written under the older, looser limit still has to come back out of the system that stored it.
- Schema:
apps/api/schemas.py(DisplayName,MAX_DISPLAY_NAME_LENGTH,MAX_DISPLAY_NAME_WORD_LENGTH) - UI:
apps/ui/src/lib/validation.ts(validateDisplayName,LIMITS)
Configuration Exchange
Hegemony supports bulk Configuration Exchange of flow definitions, device inventory, and platform configuration as YAML or Git-backed bundles. This enables version-controlled backups, environment promotion, and sharing of flow libraries.
- API:
POST /config-exchange/operations·GET /config-exchange/schema·GET /config-exchange/projection
Git Integration
Git Repository
A registered external Git remote (HTTPS or SSH) that Hegemony can clone, fetch from, and push to. Credentials are stored as Secret References and resolved just-in-time.
- Code:
apps/api/models.py(GitRepository) ·apps/api/routers/git_repositories.py - API:
GET/POST/PATCH/DELETE /git-repositories·POST /git-repositories/{id}/test-connection
Sync Mode
Controls the direction of synchronisation between a linked flow and its Git path.
| Value | Behaviour |
|---|---|
synced | Bidirectional — auto-push on commit, pull available |
readonly | Git → Hegemony only — local edits blocked, pull-only |
detached | Linkage preserved, no sync occurs |
- Code:
packages/core/enums.py(SyncMode)
Pull Sync
Reading a flow definition and its attachments from Git into the Hegemony database draft. Detects dirty-draft conflicts when the local draft has uncommitted changes.
- Code:
apps/api/services/sync/flow_git/pull.py(sync_flow_from_git) - API:
POST /flows/{id}/sync-from-git
Push Sync
Writing the current committed flow version to Git in exploded format, committing, and pushing. Detects stale-remote conditions.
- Code:
apps/api/services/sync/flow_git/push.py(push_flow_to_git) - API:
POST /flows/{id}/push-to-git
Async Push
A background mechanism where push operations are enqueued as sync history rows with status=queued and processed by the API's dispatcher loop, so that committing a synced flow doesn't block on the Git push.
- Code:
apps/api/services/sync/flow_git/queue.py(run_dispatcher_loop)
Exploded Format
The on-disk directory layout used when storing flows in Git:
<path>/<flow_slug>/flow.yaml
<path>/<flow_slug>/attachments/<filename>Human-readable, diff-friendly, and editable from any IDE.
- Code:
apps/api/services/sync/serialization/layouts/exploded.py
Git Linkage
The set of columns on flow_definitions (git_repo_id, git_path, git_branch, sync_mode, last_synced_sha, last_synced_at) and flow_attachments (git_repo_id, git_file_path, last_synced_sha, last_synced_at) that tie a flow to a Git repository path.
Sync History
An audit log of every pull or push operation. Each record tracks direction, status (queued / running / succeeded / failed), trigger source, commit SHA, timestamps, and error detail. Also serves as the queue substrate for async push.
- Code:
apps/api/models.py(GitSyncHistory) - API:
GET /flows/{id}/git-sync-history·GET /git-repositories/{id}/sync-history
Stale Remote
A condition during push sync where the remote branch has advanced past the flow's last_synced_sha. Hegemony attempts a fast-forward; if it fails, the push is rejected and the user must pull first.
Dirty Draft
A condition during pull sync where the local flow draft contains uncommitted changes that would be overwritten by the incoming Git content. The user is prompted to commit or discard before the pull can proceed.
Acronym Quick-Reference
For detailed definitions of auth acronyms see architecture/auth.md — Glossary.
| Acronym | Full Form |
|---|---|
| ABAC | Attribute-Based Access Control |
| AD | Active Directory |
| API | Application Programming Interface |
| BFF | Backend-for-Frontend |
| CI/CD | Continuous Integration / Continuous Deployment |
| CLI | Command-Line Interface |
| CORS | Cross-Origin Resource Sharing |
| gNMI | gRPC Network Management Interface |
| IdP | Identity Provider |
| JWKS | JSON Web Key Set |
| JWT | JSON Web Token |
| KV | Key-Value (OpenBao / Vault secrets engine) |
| LDAP | Lightweight Directory Access Protocol |
| NETCONF | Network Configuration Protocol |
| OIDC | OpenID Connect |
| PKCE | Proof Key for Code Exchange |
| RBAC | Role-Based Access Control |
| RS256 | RSA Signature with SHA-256 |
| SAML | Security Assertion Markup Language |
| SPA | Single-Page Application |
| SSE | Server-Sent Events |
| SSH | Secure Shell |
| SSO | Single Sign-On |
| TLS | Transport Layer Security |
| TTL | Time To Live |
| UUID | Universally Unique Identifier |
| XSS | Cross-Site Scripting |