ctlflow

Node, C# and Rust

Implementation differences, dependencies and supported platforms.

Source reference: docs/architecture/native-implementations.md at 52bf0279

The repository has Node, C# and Rust implementations of crossfire, fleetd and fleet. C# targets .NET 11 and produces managed and NativeAOT distributions from the same source. Rust distributions are built with Cargo. Shared tests run against explicitly selected distributions.

Source layout#

services/
  crossfire/
    node/{apps/server,packages,tests,scripts,tools,Dockerfile}
    csharp/{apps/Crossfire.Server,packages,tests,build,scripts,Dockerfile}
    rust/{apps/server,packages,tests,scripts,Dockerfile}
    apps/web/
    database/management/sqlite/
    knexfile.management.mjs
    tests/contracts/
    scripts/
    docs/
  fleetd/
    node/{apps/daemon,packages,tests,scripts,tools}
    csharp/{apps/Fleetd.Daemon,packages,tests}
    rust/{apps/daemon,packages}
    tests/contracts/
  fleet/
    node/{apps/cli,packages,tests,scripts}
    csharp/{apps/Fleet.Cli,packages}
    rust/{apps/cli,packages}
    tests/contracts/
scripts/

The web application, database migrations and contract tests are shared. Language-specific code is inside each product’s implementation directory.

ConcernShared contractImplementation difference
APISame routes, input validation, errors and response fieldsNode HTTP / ASP.NET / Axum
DomainFunctional operations, explicit ports, expected failure valuesTypeScript modules / C# static modules and records / Rust modules and values
DataSame SQLite file and normalized schemaTinqer / EF / Diesel
MigrationsOne service-owned Knex imageNo runtime or EF migrations
UIOne React project and built assetsNone
Native sessionsfleetd-owned Codex/Pi adaptersNative process and transport libraries
Execution compositionfleetd execution adapter, not daemon-domain codeLanguage-local adapter projects
DistributionStandalone products and shared tool bytesnpm archives / managed or NativeAOT trees / native Cargo trees
Server deploymentOne Docker package format, shared migration image and image-owned readinessBackend-specific runtime image, never language-specific site activation
VerificationOne external scenario bankSeparate internal unit/compiler tests

The selected implementation never invokes another backend as a fallback. The shared JavaScript agent tool and Pi extension are declared runtime assets; the common Knex image is an explicit migration dependency.

Server operator#

All three implementations reject repeated options, including identical values, and accept explicit ports only as canonical ASCII decimal integers in 1024..65535. Argument rejection precedes state-lock creation and native engine operations. Operator failures use a standard-error diagnostic and normal exit code 1, not a native abort or crash dump. The same external operator scenarios exercise all three boundaries.

One cross-language private SQLite lock owns an instance’s entire operator lifetime. A competing live start fails before touching containers or data. All three operator implementations consume –package. The package selects the runtime independently of the operator language. Source compilation is a build step, not another deployment path. Exact archive/image/source identities and release labels are verified before stopping writers. Current packages target Linux amd64 only. After launcher death, the next owner inspects both deterministic container names, validates their directory labels, then stops and removes their exact IDs. Foreign ownership or unconfirmed cleanup blocks migration and replacement.

Backup, migration and runtime startup are ordered phases. Docker automatic removal is not used. The instance lock is retained through runtime cleanup. A native agent’s process/history ownership is a separate controller concern.

HTTP boundaries#

Public mutation limits distinguish the actual client from a configured trusted ingress. Only an immediate peer listed in trustedProxyAddresses may supply one overwritten X-Forwarded-For IP. Untrusted headers cannot select a bucket. Node dispatches management requests to the same HTTP handler without another loopback proxy.

Pagination uses canonical positive decimal strings. Both public and private APIs reject signs, leading zeros, exponents, fractions, whitespace, duplicate fields and out-of-range values. Empty collections have one logical page.

Database#

Management SQLite uses application ID 1129465426, schema 17 and the applied Knex migration contract. Current metadata describes 63 application tables. Knex’s own bookkeeping tables are separate. File state and Conduit’s externally owned RocksDB are not management SQL databases.

Supported historical conversion is a forward migration, not a runtime reader. Applications reject missing, foreign, unsupported or unmigrated stores. No import/export is needed when changing between verified implementations.

.NET dependencies#

The SDK is pinned to 11.0.100-rc.1.26425.128; EF/SQLite packages are pinned to 11.0.0-rc.1.26425.128. Managed initial deployment is permitted. There is no automatic managed fallback inside a NativeAOT executable.

NativeAOT builds emit trimming and dynamic-code diagnostics from EF and BCL reflection paths. Supported fixed queries use generated model and query metadata. Tests execute the AOT binaries with dynamic code and reflection-based JSON disabled.

FamilyRelevant dependency surface
IL2026Trimming annotations on EF/query entrypoints
IL3050, IL3051Dynamic generic/expression paths
IL2046, IL2092, IL2093Interface and implementation annotation mismatches
IL2055–IL2111 familiesGeneric reflection and metadata requirements
IL3002Spatialite assembly-file discovery; no product Spatialite capability

Passing owned queries does not certify arbitrary EF queries, plugin models, dynamic extensions or a future dependency version. Query/model/pin changes require new native verification. Build-time compiler changes belong to the compiler owner, with its upstream notices retained; they are not runtime query fallbacks.

Evidence#

Verification records identify the tested Linux x64 host and Codex, Pi and Conduit versions. Inert-provider tests exercise integration, not paid-model quality. macOS, Windows, sustained load and arbitrary tool-process containment are not covered by those results.