Tickoni uses a Tickoni-owned tile orchestration boundary with a Firedancer-backed Linux full-runtime implementation.
The public orchestration model is Tickoni-owned:
- Tickoni tile ids and roles
- Tickoni topology descriptors and link contracts
- Tickoni support tiers
- Tickoni capability, audit, replay, evidence, model, tool, adapter, and execution contracts
- Tickoni runtime diagnostics and operator-facing status
The Linux full-runtime implementation should reuse Firedancer orchestration semantics through an adapter instead of rebuilding a parallel harness. The adapter may map Tickoni runtime descriptors to the Firedancer topology/run surface internally, but product code must not depend on Firedancer validator topology types, Solana tile kinds, validator config stages, validator metrics, or validator schemas.
For macOS and Windows retail runtimes, the same Tickoni orchestration boundary
applies. The implementation may use portable substitutes or fail-closed
unsupported operations. Native retail builds must not assume that
libfd_tango.a, libfd_util.a, or libfd_ballet.a compile, link, or export
the same symbols as the Linux build. Target-specific c_abi implementations
select the correct behavior at compile/link time, not only at runtime.
This document describes how tile orchestration is meant to work at the architecture boundary.
It owns launch lifecycle, Firedancer harness adapter semantics, runtime-tier implementation boundaries, registry responsibility, link discovery, health/staleness behavior, shutdown behavior, and crash visibility.
It does not own the canonical list of Tickoni tile IDs, product event flow, or
reuse/exclude decisions for validator tiles. Those are owned by
tile-topology.md.
It does not decide product policy, financial capability semantics, audit schema, replay capsule format, model-provider behavior, adapter authority, or execution approval rules. Those remain owned by the relevant Tickoni product and schema documents.
It also does not evaluate alternatives. The option analysis lives in:
doc/knowledge/rant/firedancer-orchestration-reuse.mddoc/knowledge/rant/static-inline-and-ffi.md
Tickoni has one orchestration contract with multiple implementation tiers:
Tickoni product topology
tile ids, roles, link descriptors, placement, support tier
|
v
Tickoni orchestration boundary
registry, callbacks, lifecycle, link discovery, diagnostics contract
|
+--> Linux full runtime
| Firedancer-backed adapter
| real process isolation, real Firedancer shared memory,
| real sandbox/metrics/lifecycle semantics where supported
|
+--> macOS / Windows retail runtime
portable or reduced-isolation implementation,
deterministic paper/sandbox demos, visible degraded guarantees,
fail-closed unsupported live effects
The boundary is stable. The implementation behind it is selected by target and runtime tier.
Firedancer's useful orchestration machinery is not a single primitive. It is a stack of conventions and guardrails:
- A topology describes tiles, workspaces, links, per-tile input link arrays, per-tile output link arrays, metrics storage, CPU placement, memory footprint, and sandbox-relevant details.
- A tile run descriptor provides callbacks and resource policy: footprint/alignment, privileged init, unprivileged init, run callback, allowed file descriptors, seccomp policy, file/address-space/process limits, and shutdown rules.
- The runner joins tile workspaces, runs privileged init, enters sandbox, fills link pointers from topology, registers metrics, runs unprivileged init, calls the tile run loop, and checks shutdown expectations when the run callback returns.
- The stem pattern provides the hot-loop discipline used by many Firedancer tiles: loop-top shutdown check, periodic housekeeping, credit/backpressure handling, input polling, fragment processing, output publication, and heartbeat/metric visibility.
- The supervisor path owns process/thread launch, child tracking, crash visibility, and crash-only shutdown behavior.
The Firedancer shape to preserve is the semantics of that lifecycle:
tile selected from topology
-> join required workspaces
-> privileged_init
-> enter sandbox
-> fill/join input and output links
-> register metrics or equivalent health surface
-> unprivileged_init
-> run tile loop
-> enforce shutdown/exit expectations
The Linux full-runtime adapter must preserve the ordering, guard intent, and failure behavior of these steps unless an explicit Tickoni substitute is documented and tested.
All tile receive paths must use bounded polling. No tile may perform blocking I/O (OS-level sleep, wait, futex, condition variable, or synchronous read on a file/pipe/socket) in its hot loop.
This is the fundamental constraint that makes the entire tile lifecycle correct: heartbeat, halt, shutdown, and health checks interleave with data processing because the hot loop is always returning to the stem's top-level iteration and can observe external signals at every iteration boundary.
Firedancer's FD_MCACHE_WAIT macro is the canonical example:
for(;;) {
seq_found = mline->seq; // atomic read from shared memory
*meta = *mline; // copy metadata if stable
if (stable && not_behind) break;
FD_SPIN_PAUSE(); // CPU hint, NOT a syscall
--poll_max;
if (!poll_max) return; // bounded: timed out, not blocked
}Key properties:
- No syscalls in the spin loop.
FD_SPIN_PAUSE()is a compiler hint (typicallypausex86 instruction or equivalent), not a wait or sleep. - Bounded.
poll_maxcaps iterations. When it reaches zero, the macro returns and the caller decides what to do next. The tile is never stuck insideFD_MCACHE_WAITfor more than a fraction of a second. - Spin, don't sleep. On a hit (data available), the macro exits immediately.
On a miss (no data yet), it retries in a tight loop that yields CPU to
sibling threads via
pausebut never yields the core to the OS. - Overrun-safe. If the consumer falls more than
depthbehind the producer,seq_diff > 0and the caller knows the expected metadata has been evicted. The macro does not retry forever.
The stem loop (fd_stem.c stem_run1) interleaves the following every iteration:
- Shutdown check —
STEM_CALLBACK_SHOULD_SHUTDOWN(ctx)at the top of every loop iteration. Returns immediately when shutdown is set. - Housekeeping — periodic health/metric publishing, credit replenishment,
input flow-control updates, event-map randomization. Driven by a lazy
timer (
STEM_LAZY) that fires every ~50-100ms. - Credit/backpressure check — if downstream has no credits,
continueto next iteration to recheck credits. Never blocks waiting. - Input polling — for each input link, check if a new fragment is
available via
FD_MCACHE_WAITwith a short poll budget. If no data, advance to the next input or recheck credits. - Fragment processing —
BEFORE_FRAG→DURING_FRAG→AFTER_FRAGcallbacks. These are the only place tile-specific logic executes. - Output publication — publish to downstream via
fd_stem_publish.
Every iteration boundary is an interleaving point for shutdown, health, and backpressure. A tile cannot get stuck because there is no blocking point between iterations.
Tickoni Phase 0 tiles (tkings, tknorm, tkdedu, tkpoly, tkaudt,
tkmetr, tkdiag) already follow this pattern: they process bounded event
counts, have no network/file I/O in the hot path, and use fd_topo_run_tile
or equivalent harness loops that interleave heartbeat with work.
Future tiles (tkcase, tkrepl, tkmodl, tktool, tkadpt, tkapi) must
adhere to the same discipline:
- Model access (
tkmodl): Use bounded polling on shared memory or a non-blocking queue from the LLM server proxy. Never have the model thread block waiting for a response. Responses arrive via shared memory or a pre-allocated ring buffer. - Adapter access (
tktool,tkadpt): Read and write through bounded shared-memory queues or a proxy tile that does the I/O and publishes results asynchronously. The consumer tile polls, never blocks. - API/transport (
tkapi): Use Firedancer'sfd_http_serverinfrastructure or a non-blocking event loop. Never have a handler block the tile's heartbeat/halt loop. - Replay (
tkrepl): Substitutes external effects with captured data. The replay loop is inherently bounded because it processes a fixed capsule. - CaseOps (
tkcase,tkdisp): All processing is in-memory against Tickoni-owned shared memory. No file or network I/O in the hot path.
Firedancer tiles are pinned to dedicated, isolated cores. FD_MCACHE_WAIT
spins for the entire poll_max budget on that core and then returns. The
cost is one core per idle tile — acceptable because a validator node has
many cores and each tile is latency-critical on its own pinned core.
Tickoni runs on targeted consumer hardware with limited cores. We cannot
guarantee tile-to-core exclusivity: multiple tiles share cores as threads.
If each tile spins for the full poll budget when genuinely idle, N idle
links burn N entire cores even though no work is being done.
The link primitives (consumer.zig, producer.zig) use a hybrid pattern:
1. Spin `spin_poll_max` (4096) iterations via spinPause()
2. Check stop/halt and update heartbeat via cnc
3. sleepNanos(idle_sleep_ns) — 100μs on Linux
4. Loop back to step 1
This trades ~100μs of added idle latency for a single core shared across many tiles. The sleep is bounded and interruptible — the stop and halt checks run on every iteration (after each sleep returns), so a shutdown signal cannot be missed. The 100μs sleep is the housekeeping and stop-check point, replacing Firedancer's per-tile-housekeeping on a dedicated core.
Firedancer's FD_MCACHE_WAIT achieves its "no sleep" guarantee by running
on a pinned core where spinning is a resource cost the system has already
paid. Tickoni's shared-core model cannot make that trade — it must sleep
during idle to keep the total tile-to-core ratio manageable. This is a
conscious architectural relaxation, not an oversight: it is documented in
wait.zig and enforced in the link primitives.
sleep(),nanosleep(),usleep()— OS-level sleep, blocks the entire tile thread and prevents shutdown/heartbeat from interleaving.pthread_cond_wait()orfutex()— blocks the tile thread. The stem loop cannot observe shutdown or health changes while the thread is waiting.read()on a blocking socket or pipe — blocks on EOF or no-data. UseFD_MCACHE_WAITon a shared-memory ring buffer instead.- Synchronous file I/O in the hot path — disk latency varies wildly and can block the tile for seconds. Buffer to memory and flush asynchronously.
- Any
select()/poll()/epoll_wait()withtimeout == -1— the entire purpose of the polling discipline is to eliminate indefinite waits.
This discipline is non-negotiable for any tile that participates in the Firedancer-backed or Tickoni-own harness. If a tile must interact with a blocking external system (LLM server, trading API, file system), the pattern is:
- The tile writes a request to a bounded shared-memory queue.
- A proxy tile (or Firedancer
fd_http_serverworker) reads the request, performs the blocking I/O, and writes the response back to shared memory. - The original tile polls the response queue with
FD_MCACHE_WAIT. - If the queue is empty, the tile continues to the next input or rechecks health/shutdown. It never blocks.
Tickoni reuses Firedancer infrastructure in two forms.
The Linux full-runtime tier reuses Firedancer orchestration as deeply as the Tickoni boundary allows:
- topology construction via
fd_topob.c(the real Firedancer topology builder) - workspace creation and join via
fd_wksp_* - link filling for
mcache,dcache,fseq, and related state - sandbox entry and allowed-fd/seccomp discipline where supported
- metrics registration or equivalent per-tile health visibility
- process/thread launch semantics where the selected Linux mode uses them
- crash and shutdown semantics
- stem-loop guard patterns for shutdown, housekeeping, credit, and heartbeat
- per-tile CPU placement via
fd_topo_cpus_init()+fd_topob_auto_layout_cpus()with Tickoni name arrays in priority slots
The adapter should follow Firedancer harness hooks, guards, ordering, and failure semantics. If Tickoni replaces one of those pieces, the replacement must preserve the same orchestration guarantee at the Tickoni boundary.
fd_topob.c is the real Firedancer topology builder — it is NOT Solana-shaped.
Tickoni calls it directly rather than maintaining a parallel builder. The adapter
pattern is:
-
Build the topology:
fd_topob_new(topo, "tickoni")— allocates andmemset-zeros the entirefd_topo_t, zeroing every field including Solana union fields (xdp, sock, gossip, quic, etc.). -
Create workspaces:
fd_topob_wksp(topo, name, footprint, ...)for each workspace (tickoni_wksp, metrics_wksp, etc.). -
Create links:
fd_topob_link(topo, name, ...)for each inter-tile link. -
Create tiles:
fd_topob_tile(topo, name, wksp, metrics_wksp, cpu_idx)for each Tile — the Zig wrapper exposes a 5-param signature. The C shim (tk_topob_tileinshim/topob.c) forwards to the upstream 8-paramfd_topob_tilewith the 3 Solana flags hardcoded as0(is_agave,uses_id_keyswitch,uses_av_keyswitch). This is transparent to Zig callers — they only pass 5 args. The shim is the single point where Tickoni's contract diverges from upstream. -
Wire links:
fd_topob_tile_in(topo, tile_idx, link_idx, ...)andfd_topob_tile_out()to attach each link to its tile. -
Validate:
fd_topob_finish(topo)runs structural validation (free check, link connectivity, workspace coverage) — pure generic, no Solana dependency. -
CPU placement:
fd_topo_cpus_init()→fd_topob_auto_layout_cpus()assigns each tile to a CPU core using priority phase arrays. The 4 arrays (CRITICAL_TILES[],ALWAYS[],STARTUP[],FLOATING[]) are the only Solana-specific part — they contain hardcoded tile name strings matched againstfd_topo_tile_t.nameviastrcmp. Tickoni replaces these arrays with Tickoni tile names:// Patched in fd_topob.c — CRITICAL (no HT sharing) static char const * CRITICAL_TILES[] = { "pack", "poh", "pohh", "gui", "guih", "tkings", "tknorm", NULL }; // Patched — ALWAYS (continuous pipeline) static char const * ALWAYS[] = { "backt", "benchg", "net", "sock", "quic", "bundle", "verify", "dedup", "pack", "sign", "shred", "pktgen", "tkdedu", "tkpoly", "tkaudt", NULL }; // Patched — FLOATING (monitoring, kernel-scheduled) static char const * FLOATING[] = { "netlnk", "metric", "diag", "bencho", "tkmetr", "tkdiag", "tkdisp", NULL };
The algorithm (
fd_topo_cpus_init→auto_tile_cpu→auto_layout_cpus) is fully generic: NUMA-aware core scanning, hyperthread-pair exclusion for latency-sensitive tiles, 4-phase priority ordering, and "floating" fallback for unmatched tiles (logged as warning, left to kernel scheduler). Only the name strings change. -
Launch: Pass
topoandtopo->tiles[]tofd_topo_run_tile()— the harness joins workspaces, fills link pointers, enters sandbox, runs the tile, and checks shutdown state.
Why 0 for Solana flags is safe: fd_topob_new() does memset(topo, 0),
so all Solana union fields in fd_topo_t are already zero. The launch path
(fd_topo_run_tile in fd_topo_run.c lines 49–140) reads only name,
kind_id, cpu_idx, allow_shutdown, and metrics — none are Solana union
fields. fd_topo_run_single_process() reads is_agave (lines 326, 335) but
Tickoni uses per-process execve mode, not single-process mode.
Keyswitch objects (the 3 params): Each keyswitch is a 128-byte shared memory
object with a state machine: SWITCH_PENDING → tile reads new bytes →
COMPLETED or FAILED. While Solana uses these for validator identity/authority
key rotation, the mechanism is generic and can be repurposed for Tickoni:
GCP/GCS OAuth tokens, AWS credential rotation, LLM provider API keys, or
adapter secrets stored in bytes[64]. For Phase 0 tiles that need no runtime
auth rotation, pass 0 and no keyswitch object is allocated.
The Tickoni topology harness (src/tickoni/runtime/topo_build.zig) intentionally
mirrors the Firedancer harness call sequence 1:1 — same methods, same order,
same placeholder convention — so that side-by-side code review and comparison
are trivial. When Firedancer upstream introduces an improvement to its topology
harness, the Tickoni harness maps directly to it without needing to understand
the divergence.
This deliberate redundancy is a maintainability investment, not a flaw. The goal is that a developer reading both harness files can follow the same narrative line: create topology → create workspaces → create links → create tiles → wire links → set CPU placement → finish → validate. The only difference is how each harness determines cpu_idx for each tile:
| Step | Firedancer harness (topology.c) |
Tickoni harness (topo_build.zig) |
|---|---|---|
| Build | fd_topob_new(topo, name) |
topobNew(buf.ptr, app_name) |
| Workspace | fd_topob_wksp(topo, name) |
topobWksp(topo, name) |
| Links | fd_topob_link(topo, name, ...) |
topobLink(topo, name, ...) |
| Tiles | fd_topob_tile(topo, name, wksp, metrics, 0, 0, 0, 0) |
topobTile(topo, name, wksp, wksp, 0) |
| CPU layout | fd_topob_auto_layout(topo, 0) |
topobAutoLayout(topo, cpu_idx_arr) |
| Finish | fd_topob_finish(topo, callbacks) |
topobFinish(topo) |
The 0 passed to fd_topob_tile() and topobTile() is a placeholder — not
the final cpu assignment. The Firedancer harness overwrites tile->cpu_idx
later in fd_topob_auto_layout() from its hardcoded priority arrays. The
Tickoni harness overwrites it in tk_topob_auto_layout() from the
cpu_idx_arr that the harness computed from CpuPlacement enum values
(exclusive = N, shared = N, floating). The placement source differs,
but the harness contract is identical: create with placeholder → lay out
CPUs → finish.
The shim's tk_topob_auto_layout(topo, cpu_idx_arr) iterates topo->tiles[]
and writes tile->cpu_idx = cpu_idx_arr[i] for each tile. This mirrors
Firedancer's fd_topob_auto_layout(topo, 0) which iterates tiles and assigns
from priority arrays. The internal data source is different (harness-provided
array vs. hardcoded C arrays), but the interface shape is identical:
a single auto_layout call after tile creation, before finish.
This matters for porting: when Firedancer changes fd_topob_auto_layout() to
support NUMA-aware groupings or dynamic CPU allocation, the Tickoni harness
already calls an identically-named wrapper at the identically-positioned
harness step. The shim absorbs the implementation difference; the harness does
not need to change.
What changes in upstream fd_topob.c: Only the 4 name arrays get Tickoni
tile names appended. No logic changes, no new functions, no struct modifications.
The file set is tracked by engine-check-changes to catch any upstream drift.
Tickoni-owned Zig code crosses into Firedancer or vendored C only through
Tickoni-owned tk_* shims and Zig wrappers under src/tickoni/c_abi/**.
Current and expected substrate categories include:
- Tango queues and control state:
mcache,dcache,fseq,cnc, and, where justified,fctlandtempo - workspace and shared-memory mechanics
- sandbox primitives on Linux
- low-level metrics/diagnostics mechanics or patterns
- Ballet protobuf/hash primitives and vendored JSON where Tickoni deliberately reuses them
The C ABI is not a place for financial policy, product topology, capability rules, adapter authority, or audit/replay semantics.
The Firedancer harness src/disco/topo/fd_topo_run.c exposes a single entry
(fd_topo_run_tile) that implements a 3-layer tile lifecycle. Tickoni mirrors
this with tile_process.zig through the c_abi bridge:
┌─────────────────────────────────────────────────┐
│ Tickoni tile_process.zig (our harness) │
│ - self-exec supervisor │
│ - join wksp → privileged_init → sandbox │
│ - join mcache/dcache/cnc/fseq/fctl/tempo │
│ - unprivileged_init → work loop │
│ - heartbeat/halt loop │
└─────────────────────────────────────────────────┘
│ calls through c_abi bridge
┌─────────────────────────────────────────────────┐
│ c_abi bridge (src/tickoni/c_abi/) │
│ - wksp.zig + shim/wksp.c → fd_wksp_* │
│ - dcache.zig + shim/tango.c → fd_dcache_*│
│ - fseq.zig + shim/tango.c → fd_fseq_* │
│ - fctl.zig + shim/tango.c → fd_fctl_* │
│ - tempo.zig + shim/util.c → fd_tempo_* │
│ - cnc.zig + shim/tango.c → fd_cnc_* │
│ - queue.zig │
│ - boot.zig + shim/ballet.c → fd_boot/ │
│ - sandbox.zig + shim/sandbox.c → fd_sandbox │
│ - topo_run.zig (fd_topo_run_tile) │
│ - topob.zig (fd_topob_* topology builder) │
│ │
│ NEW additions needed: │
│ - metrics.zig + shim/tango.c → fd_metrics_* │
└─────────────────────────────────────────────────┘
│ links against
┌─────────────────────────────────────────────────┐
│ Firedancer infrastructure (unchanged) │
│ src/tango/{mcache,dcache,fseq,cnc,fctl,tempo} │
│ src/util/{sandbox,util} │
│ src/disco/metrics │
└─────────────────────────────────────────────────┘
The diagram shows that Tickoni only needs to add 1 Zig wrapper + shim
(metrics) to cover the full Firedancer substrate used by the harness.
No structural changes to Firedancer are required.
The c_abi bridge also includes topo_build.zig and topology_spec.zig,
which are not adapter modules themselves but core orchestration components:
-
topo_build.zig(src/tickoni/runtime/topo_build.zig): builds a real Firedancerfd_topo_tfrom Tickoni's ownTopologyby calling throughc_abi.topob'sfd_topob_*wrappers. Called identically by the supervisor (parent) and each self-exec'd tile process (child) to rebuild an identical topology — the "topology handoff" pattern (V2.14.S8.T12). ReturnsBuiltTopowith allocatedbuf, opaquetopo, workspace index, per-tilecnc_obj_id[], and per-channellink_obj_id[](mcache/dcache/fseq). Does not create the workspace or instantiate object content — that is the caller's job viatopobCreateWorkspace/topoWkspNew(parent-only, once). -
topology_spec.zig(src/tickoni/runtime/topology_spec.zig): small serialized wire format (magic0x544b5453/ "TKST", version 1) for passing topology between parent and child processes.TopologySpeccarries tile ids, CPU placements, channel src/dst/depth/mtu, and workspace name.fromTopology()converts a runtimeTopologyinto the wire format;toTopology()reconstructs it. File I/O includes magic/version validation and fail-closed on truncation. Parent writes once; each child reads, rebuilds an identicalfd_topo_t, and finds its own tile index in it. -
cpu_placement.zig(src/tickoni/runtime/cpu_placement.zig): implements the Tickoni CPU placement model withCpuPlacementenum variantsexclusive,shared, andfloating, plusvalidateStatic()that enforces exclusive collision detection and shared/exclusive co-existence rules.
Firedancer infrastructure decomposes into 11 reuse categories. The first 4
require only tk_* shims (already covered or trivial). The remaining 7
require deliberate Tickoni implementation or are explicitly not reused:
-
fd_topo_run_tileharness — reuse the Firedancer lifecycle (harness) by wiring a minimalfd_topo_run_tile_tthrough the adapter. Thetile_process.zigpath is equivalent and may be used instead when harness reuse proves too expensive. -
Topology arrays —
fd_topo_tile_t[32],fd_topo_link_t[256],fd_topo_wksp_t[4]— the array-based topology is a direct match for Tickoni's link-array model. The harness readsname,kind_id,cpu_idx,allow_shutdown,metrics— no Solana union fields touched. -
fd_topo_run_single_process— single-process mode for testing, not for Linux full-runtime. Use when testing topology without process launch. -
generate_filters.py— Firedancer's CPU placement script. Resolved: the script is topology-independent. It readstopology_name,topology_path,tile_name,cpu_layout_file, andoutput_file. It does not import Firedancer types. It can be used as-is and is not part of the Firedancer API surface. -
Tile registry —
fd_topo_run_tile_tserves as a per-tile registry. Tickoni implements this insrc/app/tickoni/tile_registry.zig(nottopology.zig). The registry is keyed by tile ID and owns: thread-mode run callback (RunFn), process-mode run callback (ProcessFn), counter schema (CounterSchemaEntry), expected link cardinality (in_cnt/out_cnt), and diagnostics naming. Beyond lookup and cardinality, the registry also owns process-mode dispatch wiring (link-joining shape per tile — each process-mode entry function joins the correct consumer/producer links and calls into the pipeline stage) and topology-bijection validation (validate()checks that every topology tile is registered, every registered tile appears in the topology, and per-tile channel counts match the registry). No supervisor, process dispatcher, metrics reader, or test harness should recreate these mappings. -
Heartbeat semantics —
cnc_update/FD_CNC_HEARTBEAT— the pattern is proven; Tickoni'stile_process.zigalready implements heartbeat during work. -
Shutdown semantics —
cncshutdown flag checked inside tile work loop, not only outside — preserved in Tickoni. -
Crash-only lifecycle — crash teardown follows Firedancer semantics via
fd_stemteardown, but Tickoni implements this intile_process.zig, not throughfd_stem. The crash teardown pattern is preserved. -
Stem loop (multi-input multiplexer) — NOT REUSED.
fd_stem.cis a multi-input callback multiplexer with credit/burst accounting tied to Solana validator tile semantics. Tickoni implements its own single-input bounded polling per tile. -
Supervisor (fork+exec + wait4) — NOT REUSED.
run.c/run1.cmanage PID namespaces, seccomp, and process lifecycle. Tickoni'ssupervisor.zigowns this independently. -
Metrics (fd_metrics_*) — reuse Firedancer metrics primitives through a new
metrics.zigwrapper + shim, similar to the queue wrappers. The infrastructure is clean (Firedancer-owned, no Solana semantics).
The following concrete findings were verified during audit S8:
- fd_topo_run_tile has zero fd_topob references: confirmed via
nminspection oflibfd_disco.a— nofd_topob_*symbols are called from the harness. The harness is standalone with respect to the topology builder. - fd_topo_run_single_process is the only fdctl coupling: it is the single
file in
src/disco/topo/that includessrc/app/shared/commands/run/run1.c. This is the only cross-boundary dependency between the harness and fdctl. Mitigated by Tickoni's per-process execve model (not single-process mode). - generate_filters.py is topology-independent: it takes
topology_name,topology_path,tile_name,cpu_layout_file,output_fileas CLI arguments. No Firedancer types are imported. It can be used as-is. - fd_topo_run_tile can be called without fd_topob.c: the harness does not
depend on the topology builder. Topology can be constructed independently and
passed to
fd_topo_run_tile. - Key switch uses 128B union fields: confirmed from header inspection. Solana union fields are zeroed by memset, which is safe for Tickoni.
- fd_topo_run.c has zero fd_topob calls: confirmed via grep. The harness
uses
fd_topo_cnc_fseq_initfromfd_topo.c, but this is the shared infrastructure file — not the builder.
fd_topob.c is a generic topology builder that is usable for Tickoni:
- fd_topob_new() — memset to zero, sets app_name. No Solana.
- fd_topob_wksp() — generic workspace declaration. No Solana.
- fd_topob_link() — creates link entry, mcache/dcache objects. No Solana.
- fd_topob_tile() — creates tile entry. Takes
is_agave,uses_id_keyswitch,uses_av_keyswitchas int params. Pass0for all three. Only Solana touch: 3 trivial int params. - fd_topob_tile_in()/fd_topob_tile_out() — generic link wiring. No Solana.
- fd_topob_finish() — structural validation (workspace uniqueness, link reachability, no duplicates, no self-loops). No Solana.
- fd_topob_auto_layout() — hardcodes Solana tile name arrays (
FLOATING[],STARTUP[],ALWAYS[],CRITICAL_TILES[]). Solana names only. The algorithm (NUMA scanning, HT-pair exclusion, 4-phase priority ordering) is generic and should be reused with Tickoni tile groups.
Verdict: Use fd_topob.c with 3 params set to 0, 2 functions reimplemented with Tickoni names. Extract and reuse the CPU placement algorithm as-is. No Solana semantics leak into Tickoni product code.
The following structural tensions were identified during S8 planning and have been resolved:
-
Scope too large for one story: S8's 10 tasks span registry, links, c_abi bridge, lifecycle migration, sandbox/seccomp, stuck-tile detection, parent-path investigation, and engine drift guard. Resolved by splitting: S8 handles the core tasks, T11 handles tempo/fctl shims.
-
tile_process.zig stays as product code: The tension was whether to replace tile_process.zig's hand-rolled loop with fd_topo_run_tile. Resolution: keep tile_process.zig as the product-level self-exec boot/heartbeat/halt loop. The adapter layer (in the Linux full-runtime adapter) may optionally call fd_topo_run_tile, but tile_process.zig itself remains a product tile lifecycle implementation, not the adapter.
-
tempo/fctl shims explicitly scoped: Audit 8's plan requires them for proper stuck-tile defense. Resolved as V2.14.S8.T11.
-
Seccomp policy generation risk resolved: generate_filters.py is completely topology-independent. Input is a flat .seccomppolicy text file; output is a standalone C header. No topology context needed.
Tickoni also reuses Firedancer orchestration patterns even where the exact C implementation is not used:
- topology owns graph cardinality
- tile registry is the single answer for what a tile id does
- input and output links are arrays, not one-off fields
- shutdown is checked inside tile work, not only outside it
- heartbeat and health are updated during work/housekeeping
- reliable links backpressure or fail closed instead of silently dropping
- crash-only behavior keeps failure handling explicit
- operator diagnostics must identify which tile, workspace, link, and process is unhealthy
Tickoni owns all product and framework semantics above the generic engine layer.
Tickoni tile identities are product topology concepts, not aliases for
validator tiles. The canonical tile ID list and tile responsibility table live
in tile-topology.md. This document uses those IDs only to
describe orchestration behavior.
Tickoni topology owns:
- which Tickoni tiles exist in a product topology
- tile instance ids and logical names
- channel direction
- channel backing
- depth, MTU, reliability, and overrun behavior
- workspace ownership
- input and output link cardinality
- CPU placement policy at the Tickoni support tier
Topology is the graph. Bootstrap records are not the graph.
Process boot may carry identity and minimal startup data, but the topology is the source of truth for link cardinality and link ownership. Fan-in and fan-out are represented as per-tile link arrays:
tile
in_cnt
in_link_id[]
out_cnt
out_link_id[]
A single-link launch field is not a valid long-term representation of Tickoni topology. It can exist only as a compatibility lane for a topology that is explicitly validated as linear.
The tile registry is the single product-facing answer to:
- this tile id's logical name
- supported runtime tiers
- thread/dev run callback, if any
- Linux full-runtime run callback or adapter entry, if any
- process/retail run callback, if any
- expected link cardinality
- counter/metric schema
- diagnostics naming
No supervisor, process dispatcher, metrics reader, or test harness should recreate tile identity mappings independently.
Tickoni owns the financial correctness layer:
- capability envelopes
- policy outcomes
- destination, account, rail, venue, market, sector, instrument, amount, exposure, frequency, holding-period, environment, and approval dimensions
- audit records and hash chaining
- evidence references
- replay substitution
- model/tool/adapter routing
- approved execution authority
These contracts must not be represented as Firedancer topology fields.
The Linux full-runtime adapter is allowed to know about Firedancer topology and run types. It is the only layer that may translate from Tickoni descriptors to Firedancer-compatible structures.
The adapter owns:
- mapping Tickoni topology into the minimal Firedancer topology/run surface required by the reused harness code
- mapping Tickoni tile registry entries into Firedancer-style tile run descriptors
- mapping Tickoni link descriptors into Firedancer link arrays and link backing objects
- mapping Tickoni placement policy into supported Linux placement behavior
- preserving Firedancer lifecycle ordering and failure semantics
- reporting adapter-private failures through Tickoni diagnostics
The adapter does not own:
- financial event schemas
- policy semantics
- audit schemas
- replay capsule semantics
- model/tool/adapter authority
- operator approval behavior
- public product topology vocabulary
If the adapter needs fake or unrelated validator values to satisfy Firedancer structures, those values are adapter-private compatibility fields. They must not become Tickoni product concepts.
macOS and Windows retail runtimes implement the same Tickoni orchestration boundary but are not expected to provide Linux-equivalent engine guarantees.
Retail runtimes may support:
- deterministic paper/sandbox demos
- policy decisions over fixtures
- audit JSONL output
- replay proof
- CaseOps review of supported demo artifacts
- mocked, fixture-backed, or replay-substituted model/tool/adapter effects
Retail runtimes must fail closed for unsupported behavior:
- live trading
- live payment or transfer execution
- Ledger writes
- direct model-provider bypass
- direct adapter bypass
- privileged execution
- unsupported sandbox or isolation claims
Target-specific bridges decide at compile/link time whether a tk_* symbol is:
- Linux-Firedancer
- portable-Firedancer
- Tickoni substitute
- unsupported/fail-closed
- decision-needed
Runtime checks remain necessary for operator diagnostics, but they are not sufficient when a Firedancer symbol cannot compile or link on the target.
Every runtime tier must expose enough health information to answer:
- which tile is running, halted, failed, or stale
- which process/thread owns that tile
- which workspace or allocation backs its state
- which links it consumes and produces
- which heartbeat or health timestamp is advancing
- whether shutdown was requested and observed
- whether a failure was crash-only, clean halt, unsupported tier, or startup validation failure
Linux full-runtime tiles should preserve Firedancer-style lifecycle behavior:
- heartbeat or equivalent health state advances during work
- shutdown is checked inside tile loops and waits
- reliable consumers/producers do not block forever without a visible stale health state
- one tile crash is visible to the supervisor
- crash-only policy tears down or marks the topology according to the selected support-tier semantics
Retail runtimes must expose degraded guarantees explicitly. If they cannot provide process isolation, namespace sandboxing, CPU pinning, or shared-memory equivalence, the missing guarantee is part of the runtime tier and must be visible in diagnostics and evidence.
The Linux adapter depends on Firedancer harness behavior staying understood. Tickoni should maintain a watched Firedancer harness file set and a repo gate that detects changes relative to the previous commit. The intended command is:
just engine-check-changes
That gate belongs in just tests-all once implemented. It should force human
review or explicit acknowledgement when watched harness files change.
The initial watched set is:
src/disco/topo/fd_topo_run.csrc/disco/topo/fd_topo.hsrc/disco/topo/fd_topo.csrc/disco/stem/fd_stem.csrc/app/shared/commands/run/run.csrc/app/shared/commands/run/run1.csrc/app/shared/boot/fd_boot.csrc/util/sandbox/fd_sandbox.csrc/util/sandbox/fd_sandbox.hsrc/util/sandbox/fd_sandbox_private.hsrc/disco/metrics/fd_metrics.csrc/disco/metrics/fd_metrics.hsrc/disco/metrics/fd_metrics_base.h
The list is conservative and may be refined when the Linux adapter's exact dependency surface is known. The guard does not freeze Firedancer. It prevents silent drift in orchestration semantics Tickoni relies on.
- Do not rename or repurpose Solana validator tiles as Tickoni tiles.
- Do not add Tickoni product fields to upstream Firedancer topology structs.
- Do not encode financial policy or approval behavior in orchestration bootstrap state.
- Do not treat Markdown, the analytics store, the approved execution ledger, model providers, or financial adapters as direct dependencies of the orchestration layer.
- Do not let agents, UI, model tiles, tool brokers, adapters, or future
execution paths bypass
tkpoly,tkaudt, and replay boundaries. - Do not claim macOS or Windows Linux-equivalent isolation, performance, or shared-memory behavior without explicit support-tier evidence.
Tickoni's orchestration architecture is a boundary decision:
Tickoni owns the product topology and financial semantics.
Firedancer powers the Linux full-runtime implementation where reusable.
Retail runtimes implement the same Tickoni boundary with explicit guarantees.
The architecture should make it hard to accidentally rebuild Firedancer in parallel, and equally hard to let Firedancer validator semantics leak into Tickoni's financial product model.