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.
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_idcannot 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
OperationSpecis via serialization conformance, not a crate dependency.
Open Questions
- None specific to this spec. See open-questions.md for general 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