Files
alknet/docs/architecture/flowgraph.md
glm-5.1 19b3d3a078 docs: write Phase 0 architecture foundation — ADRs 026-034, spec docs, and task updates
Phase 0a — ADRs (9 new):
- ADR-026: Transport/interface separation (three-layer model)
- ADR-027: Crate decomposition (core, secret, storage, flowgraph, napi, CLI)
- ADR-028: Auth as irpc service (AuthProtocol behind feature flag)
- ADR-029: Identity as core type (Identity + IdentityProvider in alknet-core)
- ADR-030: Static/dynamic config split (ArcSwap, ConfigReloadHandle)
- ADR-031: Forwarding policy (rule-based allow/deny, TransportKind-aware)
- ADR-032: Event boundary discipline (domain, irpc, call protocol boundaries)
- ADR-033: OperationEnv universal composition (three dispatch paths)
- ADR-034: Head/worker terminology (replace hub/spoke)

Phase 0b — New spec documents (7):
- identity.md, services.md, interface.md, configuration.md,
  storage.md, flowgraph.md, secret-service.md

Updated existing docs:
- auth.md: reference identity.md for canonical definitions, add AuthProtocol
- open-questions.md: resolve OQ-12, OQ-16, OQ-18, OQ-22, OQ-23-25
- README.md: add all new docs, ADRs 026-034

Marked 19 architecture tasks as completed.
2026-06-07 09:32:58 +00:00

5.6 KiB

status, last_updated
status last_updated
draft 2026-06-07

FlowGraph

What

The alknet-flowgraph crate provides graph data structures and operations, mapping the TypeScript @alkdev/flowgraph package's call-graph and operation-graph concepts to petgraph::DiGraph.

Why

Call graphs and operation graphs are core observability and type-safety constructs. Call graphs track request flow across services; operation graphs validate type compatibility between composed operations. The crate is pure computation (no I/O, no external state), making it safe to include in any deployment topology.

Architecture

Core Abstraction

petgraph::DiGraph replaces graphology. The mapping is nearly 1:1 for the operations used:

TypeScript (graphology) Rust (petgraph)
graph.addNode(key, attrs) graph.add_node(attrs) + key_to_index
graph.addEdge(source, target, attrs) graph.add_edge(source, target, attrs)
hasCycle() is_cyclic_directed(&graph)
topologicalSort() toposort(&graph)

A HashMap<String, NodeIndex> provides node-key-to-index lookups, mirroring the key column in the SQLite nodes table.

FlowGraph<N, E>

pub struct FlowGraph<N, E>
where
    N: NodeAttributes,
    E: EdgeAttributes,
{
    graph: DiGraph<N, E>,
    key_to_index: HashMap<String, NodeIndex>,
}

pub trait NodeAttributes: Clone + Serialize + DeserializeOwned + Debug + Send + Sync {
    fn key(&self) -> &str;
    fn set_key(&mut self, key: String);
}

pub trait EdgeAttributes: Clone + Serialize + DeserializeOwned + Debug + Send + Sync {
    fn edge_type(&self) -> &str;
}

Operation Graph (Static)

Built from OperationSpecs at startup. Answers structural questions: type compatibility, cycle detection, reachability.

pub struct OperationNodeAttrs {
    pub name: String,
    pub namespace: String,
    pub op_type: OperationType,
    pub input_schema: Value,
    pub output_schema: Value,
}

pub enum OperationType { Query, Mutation, Subscription }

Type compatibility compares output_schema (source) against input_schema (target) using jsonschema::validate(). Exact match or subtype = compatible edge. Structural mismatch = incompatible edge.

Call Graph (Dynamic)

Populated at runtime from call protocol events. Every call.requested adds a node; call.responded/call.error/call.aborted update status.

pub struct CallNodeAttrs {
    pub request_id: String,
    pub operation_id: String,
    pub status: CallStatus,
    pub parent_request_id: Option<String>,
    pub input: Value,
    pub output: Option<Value>,
    pub error: Option<CallErrorInfo>,
    pub identity: Option<Identity>,
    pub started_at: Option<String>,
    pub completed_at: Option<String>,
}

pub enum CallStatus { Pending, Running, Completed, Failed, Aborted }

Key Operations

Query Method Returns
Topological order topological_order() Result<Vec<String>, CycleError>
Cycle detection has_cycles() bool
Ancestors/descendants ancestors(), descendants() Vec<String>
Status filtering filter_by_status() Keys with matching status
Duration duration() completed_at - started_at

DAG Invariants

  • Operation graph: DAG-only enforced at construction. Cycles throw CycleError.
  • Call graph: DAG by design. parent_request_id cannot create ancestor cycles.
  • No parallel edges: multi: false.
  • No self-loops: allow_self_loops: false.

Integration with alknet-storage

Call graphs and operation graphs are stored as metagraph instances in alknet-storage. The bridge is serialization: FlowGraph serializes to serde_json::Value, which storage persists in the nodes.attributes and edges.attributes columns.

Integration with alknet-core (Call Protocol)

The call protocol's EventEnvelope drives call graph construction:

call_map.on_requested(|event| {
    call_graph.update_from_event(&CallEvent::Requested(event));
});

Crate Dependencies

[dependencies]
petgraph = "0.x"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
jsonschema = "0.x"
thiserror = "1"
uuid = { version = "1", features = ["v4"] }
chrono = { version = "0.x", features = ["serde"] }

Does NOT depend on alknet-core, alknet-storage, or alknet-secret.

Interface Back to Core

OperationSpec and CallNodeAttrs types must match alknet-core's definitions. The bridge is serialization — flowgraph serializes to JSON, storage persists it. alknet-flowgraph does not depend on alknet-core as a crate; it conforms to the OperationSpec schema independently.

Constraints

  • Pure computation crate — no I/O, no database, no external state.
  • No dependency on alknet-core, alknet-storage, or alknet-secret.
  • Type compatibility with alknet-core's OperationSpec is via serialization conformance, not a crate dependency.

Open Questions

Design Decisions

ADR Decision Summary
027 Crate decomposition alknet-flowgraph is independent of core, storage, secret

References

  • research/flow.md — Full FlowGraph, operation graph, call graph design
  • research/integration-plan.md — Phase 2.3
  • call-protocol.md — EventEnvelope, PendingRequestMap
  • @alkdev/flowgraph — TypeScript call-graph and operation-graph implementation
  • @alkdev/operations — OperationSpec, CallHandler, registry