Reaktor Graph Sources - bring existing systems into the graph
The Reaktor Living Graph is one logical, heterogeneous, nested port graph spanning definition, build, runtime and operations. Native composition and imported systems contribute facts to that same graph; source, service, runtime and delivery layers are projections, not separately authored architectures. Reaktor should not require a rewrite before it can understand an existing system.
If a product has a Python service, a Go gateway, a Spring monolith, and some Reaktor-native code, the desktop graph should still show the whole thing. The difference is what is known about each part, at which revision and scope, and which operations are applicable and authorized.
The rule is simple:
Provenance explains where a fact came from. Coverage explains what was examined. Neither grants authority or proves that an operation is applicable.
Source adapters can feed existing systems into the Reaktor graph, and the safety rules that keep the graph honest.
The Short Version
A graph source is anything that can describe part of a system to Reaktor.
For example:
- a Reaktor app can describe itself directly;
- an OpenAPI file can describe a service interface;
- a source scanner can find handlers, classes, routes, and calls in Python or Go;
- traces can show which services talk to each other at runtime.
Every fact that enters the graph must record its origin, meaning and limits:
| Question | Why it matters |
|---|---|
| Where did this come from? | Preserve the source artifact, extractor/version, evidence reference and derivation. |
| What does it establish? | A declaration, desired binding, observed call and inferred relationship are different claims. |
| What was covered? | Record revision, target, scopes/files/partitions, unsupported constructs, missing access and truncation. |
| What assurance supports it? | Record schema checks, compiler resolution, tests or attestations for the exact claim and scope. |
| When does it apply? | Record definition revision, activation/instance, source watermark, observation time and expiry/freshness policy. |
These fields determine whether a fact can support a particular query or decision. Separately, an operation needs a supported executor, compatible target/state, current preconditions, principal authority, policy and a reviewed plan where required. A fresh trace can prove that one call occurred; it cannot prove complete topology. An old scan of an unchanged, pinned revision may still be valid for that revision.
The transport these sources feed, and the provenance a fact must carry when it crosses a runtime boundary, are specified in Reaktor Graph Protocol — native runtimes contribute over a live trunk, the other three source kinds contribute as overlays on the same model.
Current State
This is mostly a design today. A few useful pieces already exist:
| Piece | Status |
|---|---|
| Desktop source navigation | Kotlin-oriented runtime source indexing exists; it is not a general multi-language semantic compiler. |
Manual ServiceNode wrapper | Exists: projects typed service handlers and API objects into graph ports; HTTP clients and local server handlers are implemented. |
| Cloud inventory providers | Exist for config and cloud resources, not source code. |
| Unified graph provider model | Designed, not implemented as a full registry yet. |
| Python/Go/JavaScript/Spring source scanning | Not built. |
| Runtime service-map ingestion | Not built. |
| Generated wrappers from OpenAPI or gRPC | Not built. |
| Graph document editing | Immutable GraphDocument commands and GraphEditorSession undo/redo exist; the document canvas entry point remains view-only. |
| Canonical graph commit and runtime activation | Not implemented by the current editor commands, runtime connect or exports. |
So this document is not claiming that foreign-code ingestion already ships. It defines the path from the pieces that do exist to the system Reaktor needs.
Four Ways A System Enters The Graph
Reaktor should label each claim by how it was learned. Several sources can describe the same logical element; they are coexisting evidence channels, not a four-level authority ladder.
| Source kind | Plain meaning | Example | What it does not establish |
|---|---|---|---|
| Native | Reaktor code describes its composition or contracts. | A Kotlin or JS Reaktor node. | Complete extraction, production health, hot-reload compatibility or permission to change it. |
| Wrapped | A service has an explicit Reaktor contract/proxy boundary. | A Go payments API behind a reviewed client. | Idempotency, safe retries, complete internals or authority to invoke/deploy it. |
| Source-scanned | A scanner derives facts from pinned source files. | Python routes, Spring controllers, Go handlers. | Complete dynamic call resolution or a currently deployed runtime. |
| Runtime-observed | A source records behavior in a specified runtime/time window. | OpenTelemetry evidence of service A calling service B. | Unexecuted paths, complete topology, source ownership or permission to inspect sensitive payloads. |
The important safety rule:
No source kind grants actions automatically. Enable an operation only when its executor, applicability, preconditions, evidence requirements and authority are satisfied for the selected revision, environment and principal.
This prevents Reaktor from acting on guesses. Seeing a service in traces is useful. It is not enough evidence to rewrite, deploy, or delete anything. Conversely, a source-scanned service can have an authorized, verified deployment adapter without becoming Reaktor-native. A native node can remain inapplicable to hot reload because its target or state migration is unsupported.
How Existing Code Moves Toward Reaktor
Evidence and integration can accumulate around one logical service:
logical service identity
/ | | \
source facts interface runtime operation adapters
revision schema instances and contracts
A practical example:
- Reaktor sees a legacy billing service in traces.
- A source scanner links that service to
BillingController.java. - OpenAPI or gRPC reflection gives Reaktor a typed interface.
- Reaktor creates a
ServiceNodewrapper around that interface. - A new Reaktor-native implementation is built behind the same boundary.
- Traffic is compared between old and new implementations.
- An explicit cutover/removal plan retires the old implementation after the required compatibility, traffic and state-migration checks pass.
That is the point of graph sources: do not start with a rewrite. Start by making the existing system visible, then strengthen or replace boundaries where useful. Native conversion is optional; the product should remain operable with heterogeneous implementations.
What A Graph Source Emits
The exact Kotlin model belongs to the unified graph design. This is a proposed contract sketch, not current callable APIs:
interface SystemGraphProvider {
val source: String
suspend fun fragment(request: GraphSourceRequest): GraphFragment
}
data class GraphFragment(
val sourceRevision: SourceRevision,
val coverage: SourceCoverage,
val claims: List<GraphClaim>,
val operationDescriptors: List<OperationRef>,
)
data class SystemGraphNode(
val id: String,
val kind: String,
val title: String,
val sourceLocation: SourceLocation?,
val provenance: List<Provenance>,
val operations: List<OperationRef>,
)
For this page, the important fields are:
source: the importer, such asreaktor-native,openapi,otel, orsource.python;sourceLocation: the file and line to open when a user clicks the node;provenance: evidence, extraction method, claim meaning, assurance and applicable revision/time;coverage: what was examined, what is missing/unsupported, source watermarks and completeness for the requested domain;operations: advertised operation descriptors, not credentials or a list of automatically permitted actions.
The graph registry reconciles facts through explicit stable IDs, artifact fingerprints and reviewed aliases. A billing API can have source locations, an OpenAPI schema, wrapper artifacts and observed runtime instances linked to one logical service. Do not merge merely by a similar name or URL, and do not merge a definition with its many activations. Conflicting claims remain visible with provenance; desired and observed values do not overwrite each other.
Exact queries require a specified snapshot vector across definition revision, activation epochs and participating source/index generations or watermarks, with complete coverage of the declared query domain. Missing source access, a cursor gap, truncation or stale dependent evidence yields a partial/unknown result. A vector alone does not prove a globally consistent cut across independently observed systems; the query must state the consistency guarantee it can establish. See visitor snapshot consistency.
Source Scanning
Source scanning is how Reaktor learns from non-Reaktor code without running it.
The first useful version does not need to solve every language perfectly. It should find the things developers expect to click:
- files;
- modules or packages;
- classes;
- functions;
- routes and handlers;
- service calls;
- imports and dependencies.
The recommended implementation path:
- Use tree-sitter for fast syntax-level extraction across many languages.
- Add SCIP or CodeQL where compiler-grade references are needed.
- Emit one common graph shape instead of inventing one schema per language.
- Always attach
sourceLocationso the desktop can open.py,.go,.java,.ts, or other files directly.
Source-scanned facts do not themselves authorize mutation. A scanner can be wrong and a call graph can be incomplete, so pin the source revision, extractor/schema version and coverage. A supported local refactoring tool may still propose an edit against those exact sources; compilation/tests and an authorized application path establish whether that edit is applicable. Scanned is provenance, not permanent read-only status.
Runtime Observation
Runtime observation is how Reaktor learns from a system that is already running.
The first runtime source should ingest OpenTelemetry service graph data:
- services become nodes;
- calls become edges;
- latency, error rate, and request rate become useful node metadata.
Runtime observations are scoped and potentially delayed, sampled or incomplete. They show recorded paths, not every possible path. Preserve environment, activation, time window, sampling/coverage, watermarks and freshness. A missing edge cannot prove a dependency absent. Observations support dashboards, discovery, diagnosis and operation preconditions where the operation's contract accepts that evidence; they do not grant authority.
OpenAPI and gRPC reflection are slightly different. They do not show source, but they can recover a typed interface. That interface is what lets a runtime-only element gain a reviewed wrapper. Reflection results also need target/version and access provenance; they are not automatically the complete contract of every deployed instance.
Wrapping
Wrapping gives a non-Reaktor service an explicit, inspectable integration boundary.
The wrapper is a Reaktor ServiceNode proxy in front of the real service. The
foreign service keeps running as-is, but calls pass through a Reaktor boundary.
The proposed policy layer can add, where the underlying operation supports it:
- retries;
- timeouts;
- circuit breaking;
- auth context propagation;
- telemetry;
- permission checks.
The wrapper should contain translation and safety logic, not business logic.
These are policy possibilities, not guarantees implied by ServiceNode. Retrying an effectful request requires an idempotency/deduplication contract; a timeout does not prove the server stopped; a circuit breaker does not make a call durable. Record transport, request/result schema versions, authority propagation, cancellation, ordering, stream demand and delivery semantics on the boundary. Local, HTTP and durable-message realizations are interchangeable only when they satisfy the caller's declared requirements.
Today, this exists manually. The design goal is to generate much of it from OpenAPI or gRPC reflection, then let a developer review and finish the wrapper.
What Actions Should Be Allowed
The desktop evaluates each operation independently. It should explain an unavailable action using its actual missing applicability or authority condition, rather than a provenance tier.
| Operation family | Conditions to evaluate |
|---|---|
| Inspect/open source/dashboard | Authorized visibility, exact object/source binding and sensitivity policy; read operations can have effects or expose sensitive data. |
| Edit/refactor | Supported source or definition editor, base revision, applicable transform, validated patch and authorized destination. |
| Invoke/query | Registered executor, compatible operation schema, current target, principal authority and effect/data policy. |
| Deploy/restart/submit | Verified artifact/environment mapping, supported provider adapter, current preconditions, scoped authority and a fingerprinted execution plan. |
| Hot reload/runtime reroute | Compatible implementation/ABI, explicit activation support, state and in-flight-work plan, target/platform constraints and required authority. |
Enforce this in the shared executor and expose the same decision to humans and agents. A file path or native label must not unlock deploy/destructive actions. A separately verified deployment adapter can make a deploy applicable to foreign code. Definitions commit through typed patches; runtime and external effects execute through plans and produce receipts. A graph commit is not proof that an environment activated it.
Implementation Plan
Build this in small steps:
| Step | Deliverable | Why first |
|---|---|---|
| 1 | Establish source revision/coverage contracts and per-operation applicability/authority checks; generalize source navigation to any supported file:line. | Makes imported facts useful without granting ambient actions. |
| 2 | Implement the graph provider registry, identity reconciliation and snapshot vectors. | Lets many sources describe one service without collapsing definitions, activations or conflicting claims. |
| 3 | Add one or two source scanners, likely Python and Go. | Proves non-Reaktor code can appear in the desktop. |
| 4 | Add OpenTelemetry service-map ingestion. | Shows live topology for systems without source access. |
| 5 | Generate reviewed ServiceNode wrappers from OpenAPI or gRPC. | Gives existing services safe Reaktor powers. |
| 6 | Integrate operation plans/receipts, source-change invalidation and gap recovery with the shared executor. | Keeps decisions valid across source changes and runtime epochs. |
| 7 | Add a Reaktor-native definition manifest with stable identities and source maps. | Makes native composition precise and gives refactors a diffable artifact without claiming arbitrary Kotlin is statically recoverable. |
The first real milestone should be modest:
Click a Python or Go node in Reaktor Desktop and jump to the exact source file, with pinned source revision, extractor and coverage visible, and only applicable, authorized actions enabled.
That would prove the model without pretending the whole migration system exists.
What Happened To Graphify
graphify is not the right shape anymore.
The old idea assumed the useful graph already existed inside Reaktor. Existing systems break that assumption. A real product has code, services, configs, interfaces, and traces that Reaktor did not create.
So graph sources replace graphify:
- preserve provenance, assurance and coverage as separate dimensions;
- keep the idea of graph reports and graph diffs;
- add source scanning, runtime observation, and service wrapping;
- feed everything into the unified system graph instead of a separate graph.
reaktor-compiler can still matter here. Its future role should be native
Reaktor extraction: produce a stable definition manifest and source maps, then diff that artifact during refactors. Kotlin DSL authoring remains fully expressive; evaluated manifests must capture inputs, bounds and provenance. Dynamic or effectful construction that cannot be recovered statically must be represented explicitly or evaluated in a controlled authoring environment, not guessed by a scanner. Runtime-created instances link back to declared factories and activation epochs.
Open Decisions
- Which two languages should the first source scanners support?
- Which coverage/assurance vocabulary can be shared by extractors without collapsing source origin into a trust score?
- How much wrapper code should be generated versus hand-written?
- What is the minimum source graph that is useful in the desktop?
- Which preconditions, evidence scopes and authority checks does each operation require, and which sources can establish them?
See Also
- Reaktor Unified System Graph - registry, merge rules, provenance, and operations.
- Reaktor Bus - durable async delivery that a wrapped service may publish to or consume from.
- Reaktor Server - resilience, auth, and telemetry around service calls.
- Reaktor Capability - the same safety question applied to runtime capabilities.
- Reaktor Visual Language - how the desktop can render the migration state.
Related docs
- Unified Graph - Use before building Reaktor Engine graph importers, cloud/resource nodes, operation providers, or MCP graph tools.
- Graph Visitors - Use before designing a new graph pass or selector.
- Capability Runtime - Use before branching behavior by platform, device, network, permission, or renderer state.
- Service Layer - Use before adding service APIs, splitting reaktor-service out of reaktor-graph, wiring handlers to Spring/Workers, or making modules graph-projectable without hard graph dependencies.
- Security Plan - Use before enabling deploy actions, remote agents, or privileged commands.
- Visual Language - Use when designing the node-based authoring/orchestration language and its editor, compiler, and agentic surface.