Self-hosted distributed runtime · v1.0 research

Titan

A distributed runtime for DAGs, services, and agentic workflows, shipped as a single JAR with zero external dependencies. The scheduler, the wire protocol, the failure handling, and the persistence layer are all built from scratch.

Java 17+ 132 KB JAR 33-opcode binary protocol No broker No database Apache 2.0

The problem

It started with a laptop
that ran out of memory.

The second machine was in another country. Titan began as a control plane so the first could hand work to the second, nothing more.

  • PARALLELJobs fired at the second machine. Nothing ordered them.
  • DAGThey needed ordering, and a graph was the natural way to say it.
  • SERVICESMy own servers and web apps, work that has to stay alive, not scripts that exit.
  • SELF-HOSTINGDeploying long-running work meant Titan could host itself. What came out was a self-healing, self-scaling, self-descaling lifecycle manager at the process level rather than the container level, and without adding a dependency.
  • SDKThe unlock, graphs built in code at runtime, which made the DAG the representation and agents possible.

Each step was the same problem one size larger, and something built to borrow memory had quietly become the infrastructure. That is also where it stopped being a script: workers die mid-job, masters restart, results go missing, someone has to say yes, each of those had to become a mechanism rather than a patch.

The constraint turned out not to be unusual. Teams have machines sitting idle and workloads that outgrow one box. Every tool that solves this properly assumes you already run infrastructure, the thing you were trying to avoid.

Airflow

Postgres · scheduler · webserver

Temporal

Cassandra / SQL · history service

Ray

Head node · worker fleet

Titan

One JAR

The idea · the cluster

The answer to that gap has to be a runtime with nothing to operate. Here is the shape of one.

Three components

INGRESS YAML Python SDK CLI Constructor MCP submit · TCP 9090 Titan Master scheduler · :9090 dispatch · heartbeat 10s self-register 30s · complete · logs WORKERS · capability-tagged :8080 GENERAL :8081 GPU :8090 WRK · ephemeral spawned and reclaimed by the scaler TitanStore RESP · :6379 · optional state · recovery Dashboard :5000 polls · 2s
The arrow directions are the design. Workers initiate registration, completion callbacks and log batches, so a node behind NAT or an SSH tunnel joins with no inbound firewall rule, the Master holds no static worker config and dials back only over connections the worker already established.

Core execution and routing work with the store absent, you lose state recovery and the KV bus, not scheduling.

The idea · the answer

Three components is easy to draw; the hard part was writing them. One executable was the constraint, not the outcome:

Owning every layer underneath is what buys that, and the size is the receipt: the shipped JAR is 132 KB, and the pre-shade artifact is within a kilobyte of it because there is nothing to bundle in.

Every dependency it doesn't take
is a problem it solves itself.

LayerBuilt from scratch
SchedulerDependency resolution, capability + affinity routing, event-driven dispatch
Transport33-opcode binary RPC over raw TCP, no gRPC, no Netty
ExecutionTask runners behind one interface: scripts, services, detached processes, archives, file staging
PersistenceRESP-compatible store with AOF, KV, and replication
Concurrency~4,900 lines of framework-free Java across dispatch, heartbeat, and scale loops

“Zero dependencies” means the engine: scheduler, protocol, store and execution ship in one JAR and pull in nothing. The optional layers are honest about their cost, Flask if you want the browser dashboard, PyYAML if you write pipelines in YAML, the mcp package if you connect an AI client. The SDK's own client is standard library only: sockets, structs, base64, zipfile.

Where it fits · the case for it

Before the mechanisms: whether any of this is relevant to you.

Why you'd reach for it.

And if none of those describe you, reach for the mature tool. Airflow, Temporal and Ray are excellent, and this deck will not argue you out of a system that works.

The idea · the thesis

Zero dependencies only pays off if one engine can cover more than one job. That is the thesis.

One representation. Three extensions.

A DAG is already the common execution representation across schedulers, workflow engines, and compute frameworks. Titan starts there, then extends the same object three times, because each world breaks a different assumption the base quietly makes.

nodes are units of work · edges are dependencies and the whole graph is writable at runtime BATCH breaks nothing, but the node has to know what hardware it needs + capability tags · affinity pinning · priority and delay queues SERVICES breaks “every node terminates”: a server never exits + deploy completes while the process lives on · ports · restart · teardown AGENTS breaks “the graph is known at submission”: the LLM writes it + mutation mid-run · shared memory · durable human gates
Each extension is a change to what a node is allowed to be, not a separate engine bolted alongside. That is why one runtime hosts all three, and why an agent can keep a service alive and dispatch batch jobs against it inside a single cluster.

A plain DAG assumes every node runs anywhere, finishes, and is known before you submit. Batch breaks the first. Services and agents break the other two.

Batch

Assumption held. But a node that can run anywhere is useless on mixed hardware.

Capability tags route strictly; affinity pins a child to its parent's node; a DelayQueue holds timed work at zero CPU; retry and a dead-letter queue stop one poison job stalling the graph.

Services

Assumption broken. A DAG node reports done and exits. A server does neither.

Completion was split from process lifetime: the deploy returns DEPLOYED_SUCCESS and downstream unlocks while the process runs on under a supervisor. Teardown became explicit: stop_service suppresses the restart.

Agents

Assumption broken. You cannot submit a graph whose shape the next LLM call decides.

The graph became writable mid-run: stages submit further DAGs, agent_run_id stitches them into one run, TitanStore carries state across nodes, and a gate holds the run for a human up to 48 hours.

Same scheduler. Same protocol. Same dashboard. Most orchestrators pick one world and excel in it. The narrower, testable claim: these three are the same object, and the seams are where the engineering went.

How it works · 1 of 7 · what that lets you draw

“One representation, three extensions” is a claim. These seven slides are the mechanisms that make it true.

What you can actually draw.

A job declares a list of parents and nothing else, satisfiedDeps ≥ declaredDeps, and no rule requires a submission to be connected. Every shape below is legal in a single submit_dag().

Independent no edges, all start at once Sequential each waits for the one before Fan-out one completion unblocks many Fan-in waits for every parent Diamond split, then rejoin Two roots, one sink independent lines converging Cross-branch a node with parents in two lines Disconnected one submission, unrelated work
A job with no parents is ready the instant it is submitted, which is why independent jobs are not a special case but the base case, and why one submission can carry several unrelated pipelines at once.

How it works · 2 of 7 · beyond a plain DAG

Three shapes Titan adds to the graph.

A plain dependency graph has no vocabulary for a human decision, a process that never exits, or a node that doesn't exist yet. Titan runs all three on the same scheduler, the same protocol, and the same dashboard as everything else.

Injected gate you never draw this node holds for a human children rewire onto it Service node completes, but never exits deploy returns success , downstream unlocks, process lives on Grown at runtime nodes that weren't in the source submitted mid-run by the orchestrator
Dashed marks what the source never contained. The gate is inserted by the SDK at submit time; the service node reports done while its process keeps running; the agent's nodes arrive in later submissions and are stitched into one run by agent_run_id.

Two shapes are not legal and fail quietly rather than loudly: a cycle, and a parent id that was never submitted. Both leave the job parked in the waiting room forever. The Constructor runs a DFS cycle check before deploy; the YAML and SDK paths do not.

How it works · 3 of 7 · routing

A node is now ready and legal. The scheduler still has to decide which machine runs it.

Routing knows your hardware.

How it works · 4 of 7 · the wire

The wire

Every message, client↔Master and Master↔Worker, is an 8-byte header plus a UTF-8 payload over a raw TCP socket. No HTTP, no serialization framework.

The receiver compares the version byte against its own and rejects mismatched frames rather than guessing. There is no negotiation handshake.

0x01 heartbeat 0x04 submit_dag 0x06 run 0x12 job_complete 0x55 job_status 0x60 kv_set
OffSizeField
01version, currently 1
11opcode, 33 defined
21flags, reserved
31reserved
44length, u32 big-endian
8npayload, UTF-8

How it works · 5 of 7 · failure

Failure is a transition, not an exception.

Handling partial failure is the part most from-scratch orchestrators skip. Eight mechanisms are wired through the scheduler core.

Heartbeat detection

Master dials each worker; silence past threshold marks it dead and re-queues its in-flight jobs.

Bounded retry

Up to 3 retries, 4 attempts total, then quarantine, not an infinite loop.

Dead-letter queue

Poison pills are isolated with logs intact; children are cancelled so the DAG doesn't stall.

Fail-fast

Port conflicts and rejected deploys skip the retry loop, deterministic errors don't burn attempts.

Orphan recovery

recoverState() rescues in-flight work on restart so a DAG resumes instead of stranding.

Timeouts

Process trees are forcibly terminated on expiry; 30s RPC read timeouts surface stuck peers.

Backpressure

Saturated cluster re-queues rather than dropping or overloading a node.

Backoff callbacks

Completion reports retry 5× at 1→2→4→8s so a network blip can't lose a finished result.

How it works · 6 of 7 · elasticity

The cluster resizes and repairs itself.

A reconciliation loop compares pressure against capacity every 15 s; a heartbeat loop dials every worker every 10 s. Between them the cluster grows, heals and shrinks unattended.

pressure saturated, every GENERAL worker full :8080 GENERAL permanent · never reclaimed :8081 GPU ✕ stops answering WRK-8090 ephemeral · spawned on demand 1 2 3 time →
Two independent loops produce this: the scaler reconciles capacity, the heartbeat detector reconciles liveness. Neither knows about the other.

1 · Scale up

All GENERAL workers saturated and under the ceiling, so the Master submits a job to its own scheduler, DEPLOY_PAYLOAD|Worker.jar, priority 10. A worker launches the JVM, which self-registers.

2 · Self-heal

The GPU node stops answering heartbeats, is marked DEAD and dropped, and its in-flight jobs re-queue onto healthy nodes. If it returns, its own 30-second loop rejoins it.

3 · Scale down

A reclaim candidate must be ephemeral, not the root, at zero load, hosting no service, idle over 45 s. Highest port first, one per cycle, four guards before anything you care about.

How it works · 7 of 7 · the human

The human is part of the graph.

Set one field (hitl_message) and the SDK injects a gate job after that step and rewires every downstream dependent onto it. The engine never learns what HITL is; it sees a normal DAG with an extra node.

The pipeline then pauses durably: 48-hour default, survives a closed laptop, resumes on an Approve in the dashboard. Reject cancels downstream instead of leaving it hanging.

Airflow needs custom sensors for this. Prefect needs custom state handlers. Here it's a keyword argument.

preprocess ✓→ hitl gate ⏸→ train

Approve

Gate exits 0 · downstream releases

Reject

Gate fails · downstream cancelled

Quality checks before an expensive training run. Compliance sign-off before a deploy. Editorial review before a report publishes.

Ingress

That is the whole engine. The rest is putting work into it and watching it run, starting with the five front doors.

Five ways in, one graph out.

YAML

Declarative, version-controlled pipelines. Commit to git, re-run any time.

Python SDK

Shape decided at runtime, agent loops, conditional branching, dynamic fan-out.

CLI

Deploy a YAML pipeline from the terminal, or drive the cluster from the Java shell: stats, job status, logs, cancel with cascade, approve or reject a gate, deploy a service, decommission a worker.

Visual Constructor

Drag nodes, draw edges, deploy in one click. Generates the equivalent YAML and SDK code.

MCP

11 tools over stdio. Describe the pipeline in English from Claude Desktop or Cursor; the agent writes the scripts and submits.

Same destination

All five land in the same dispatch loop and the same live visualizer, per-job logs, status, workspace files, HITL banners. The front door does not change the graph.

Start here

Five front doors, one graph. Here is the cluster they all submit to, from cold.

One command to a live cluster.

./titan-dev.sh up
$ ./titan-dev.sh up

[INFO] Booting Titan Development Environment...

[OK] Engine JAR found.
[SETUP] Verifying Python SDK...
[OK] Python SDK ready.
[START] Starting TitanStore (Port 6379)...
[START] Starting Titan Master (Port 9090)...
[START] Starting General Worker (Port 8080)...
[START] Starting UI Dashboard (Port 5000)...

====================================================
               TITAN CLUSTER IS LIVE
====================================================
[+] Master Node:     localhost:9090 (PID: 5952)
[+] Worker Node:     localhost:8080 (PID: 5991)
[+] TitanStore:      localhost:6379 (PID: 5944)
[UI] Dashboard:      http://localhost:5000
  • BUILDSCompiles the Java core and installs the Python SDK in editable mode.
  • STARTSStore, Master, a GENERAL worker, and the Flask dashboard, in dependency order.
  • STOPSOne Ctrl+C tears the whole cluster down and frees every port.

No Postgres. No broker. No container runtime. No cluster to join. The next command submits a real DAG:

python titan_sdk/titan_cli.py deploy agent.yaml

The run, live

Every pipeline is watchable while it runs.

dashboard / dags
The Titan DAG visualizer mid-run: counters reading 4 completed, 1 running, 0 failed, 6 waiting with a 36 percent progress bar, above a dependency graph whose nodes are coloured green for completed, amber for running, and grey for waiting, each labelled with the worker port executing it, and a workspace files panel listing downloadable job logs and outputs.

Node colour is live state, and every node carries the worker port that ran it, so routing decisions are visible, not inferred. The amber node here is a HITL gate holding the pipeline for a human. Dashed edges lead to work that hasn't been unblocked yet.

Same view regardless of how the DAG arrived, YAML, SDK, Constructor, or MCP. Click a node for its live log stream; the files panel below serves every artifact the run produced. Known limit: live status here is resolved through the client-side DAG manifest, which is being reworked, the graph, the logs and the file panel are reliable, but live status can lag or misreport. The Timeline and Cluster pages read spans and the store directly, so use them as the authority when the two disagree.

Observability · the scheduler

Cluster & Control Plane.

dashboard / cluster

The DAG pages describe your pipelines. This one describes the scheduler running them. A pipeline can be perfectly healthy while the control plane is the problem, and none of that shows up as a red node: it shows up as work not moving, with no stated cause. So the page is built to state the cause. Three questions, and what answers each.

The Titan Cluster and Control Plane Live tab: eleven status tiles reading 1 worker, 0 ready, 0 blocked, dispatch p50 6 milliseconds, p99 42 milliseconds, 1 dead letter in red and 33 parked in amber; a TitanStore strip showing connected, write p50 1 millisecond, zero dropped writes; and a diagnostics panel whose five tables give pending reasons naming the missing capability, capability supply against demand with two dead ends and the command that fixes them, the dead-letter queue with its failure reason, queue-wait percentiles, and the retry distribution.

Why isn’t work moving?

  • Pending reasons, in the scheduler's own words
  • Capability dead ends, with the command that fixes one
  • Four pre-dispatch lanes: delayed, blocked, ready, parked
  • Value: the cluster states its own stall instead of leaving you to infer it

Where does the scheduler's own time go?

  • Dispatch loop split five ways, with a per-phase share table
  • Store read and write latency, plotted apart
  • Saturation: occupied slots against total
  • Value: dispatch is serialized, so this is the throughput ceiling

What can no other chart show?

  • Service roster: dialable address, uptime, host, state
  • Queue wait by declared priority
  • Pipeline duration trend across runs
  • Value: a service has no duration, and priority ordering was unobservable

Example 01 · declarative

That is the view. Here are five real pipelines that produce it, from the simplest upward.

Four scheduling primitives, one file.

titan_test_suite / dag_structure_test
agent.yamlthe file the quickstart runs
name: "titan-comprehensive-test"
project: true            # zip the surrounding folder

jobs:
  - id: "step-1-init"
    file: "scripts/init.py"
    priority: 10            # jumps the queue

  - id: "step-2-fetch"
    file: "src/processor.py"
    delay: 2                # ClockWatcher holds it 2s
    depends_on: ["step-1-init"]

  - id: "step-3-process"
    file: "src/processor.py"
    affinity: true           # same node as its parent
    depends_on: ["step-2-fetch"]

  - id: "step-4-server"
    type: "service"         # stays alive, auto-restarts
    file: "src/server.py"
    port: 9000
    depends_on: ["step-3-process"]
init→ fetch→ process→ server :9000
  • PRIORITYQueue jumping on an open scale, higher first.
  • DELAYHeld in a DelayQueue, costing no CPU until due.
  • AFFINITYPinned to the physical node that ran fetch, for local cache reuse.
  • SERVICEA long-running process in the same graph as the batch steps.

One declarative file exercises the delay queue, the priority queue, affinity routing, and the service supervisor.

Example 02 · agentic

The LLM decides whether to loop.

examples / research_agent
research_agent.pythe loop lives in the orchestrator
while iteration < max_iterations:
    iteration += 1

    # N researchers, N decided by the planner
    researcher_jobs = [
        TitanJob(
            job_id   = f"research-worker-{tag}-{i}",
            filename = _WORKER,
            args     = f"{run_id} {iteration} {i} ...",
        )
        for i in range(len(subtopics))
    ]
    client.submit_dag(f"ITER{iteration}",
                      researcher_jobs,
                      agent_run_id=run_id)
    wait_for_signals(client, researcher_keys)

    # THE AGENTIC DECISION
    decision = json.loads(
        client.store_get(f"...:eval:{iteration}"))

    if decision["decision"] == "SYNTHESIZE":
        break              # LLM says done
    else:
        subtopics = decision["gaps"]   # go deeper
[AGENT] Planner decided 4 subtopics:
  [0] Performance, Scalability, Latency
  [1] Cost and Operational Overhead
  [2] Retrieval Quality, Embeddings
  [3] Consistency, Freshness, Integration

[AGENT] Iteration 1/3, 4 subtopic(s)
[AGENT]   researcher-1-0, done
[AGENT]   researcher-1-3, done  ← out of order:
[AGENT]   researcher-1-1, done    truly parallel
[AGENT]   researcher-1-2, done
[AGENT]   evaluator-1, done
[AGENT]   Evaluator: research complete.
query Planner LLM picks N submit_dag researcher-0 researcher-1 researcher-2 N decided at runtime, not in the source Evaluator SYNTHESIZE Synthesizer report.md DEEPEN · the gaps become the next iteration's subtopics
The only edge that matters is the dashed one. SYNTHESIZE ends the run; DEEPEN respawns the researcher stage with subtopics the evaluator chose, so the graph grows a fresh parallel layer that existed nowhere in the source. Neither N nor the iteration count is written down anywhere.

Example 03 · no code

Or draw the graph and hit deploy.

dashboard / dags/new
The Titan DAG Constructor: a browser canvas with connected job nodes, a node configuration sidebar, and a live YAML and Python SDK output panel.

Drag nodes onto the canvas, drag from a node's port to draw a dependency, and set script, capability, priority, and gate message in the sidebar. A DFS cycle check blocks deploy if you wire a loop. Deploy submits straight to the running cluster, and the button becomes a link into the live visualizer.

The output panel emits the equivalent YAML and Python SDK for whatever is on the canvas, so the pipeline from Example 01 can be built by dragging, then copied back out as a committed file. The visual path is an on-ramp, not a walled garden.

Applications

Four representative pipelines. So what is it actually for?

What people actually run on it.

RAG factory

Chunk a corpus in parallel, embed on GPU workers, fan in to build the index. Vector store and embedding endpoint stay up as services.

Re-indexing is a pipeline you re-run, not a script someone babysits.

batch + service + gate

Hyperparameter sweeps

N configs train concurrently on GPU nodes, each writing its metric to TitanStore. A fan-in job picks the winner.

Wall-clock is the slowest single run, not the sum.

batch

Multi-agent research

A planner sets the subtopics, N researchers run concurrently, an evaluator loops or stops, a synthesizer writes up.

Agents span machines, so the parallelism is real and the output outlives the session.

agent

Internal inference platform

A model server runs as a permanent service with port management and auto-restart; batch jobs call it from the same cluster.

One substrate for the always-on thing and the work that calls it.

service

The pattern underneath: work that needs real compute, real time, or a real decision in the middle, and a team that would rather own the substrate than operate a platform to get it.

Shapes Titan runs today at research and medium scale, a lab or a small team, tens of workers, machines you already own. Throughput at production load is not yet measured; see What v1 is not.

Where it fits · the surface

Not a roadmap, what is implemented and running today. The full inventory is in the docs.

Everything in the box.

Scheduling

  • Event-driven DAG unlock, no polling
  • Capability routing: GPU, HIGH_MEM, custom
  • Priority, delay, affinity, saturation backpressure
  • Fan-out, fan-in, diamond, disjoint graphs

Execution

  • Scripts, services, detached processes, archives
  • Per-job isolated workspace
  • Timeouts with process-tree kill
  • Services auto-restart; explicit teardown

Failure & recovery

  • In-flight jobs on a dead worker are re-queued
  • Bounded retry, then dead-letter
  • Orphaned-job recovery across a Master restart
  • Cancel cascades to children

Observability is not an add-on: one span per dispatch attempt, queue wait attributed per job, critical path computed, and the dispatch loop split five ways — in the box, no Prometheus, no Grafana.

Validation

Benchmarks written for your own system prove very little. So we ran someone else's.

Real workflows, not ones we wrote.

WfCommons publishes DAGs recorded from production scientific workflows: astronomy, genomics, seismology, agriculture. They ship the task graph, the dependencies and the measured runtimes. Titan replays them, so correctness is a checkable fact rather than a claim we make about ourselves.

10,817 tasks

Five published instances, the largest a 9,805-task Montage with a single level 6,285 tasks wide.

all completed

12,992 edges verified

Every parent-child edge checked against the recorded spans: did the child really start after its parent finished.

0 ordering violations

Shapes, not just size

2 to 11 levels deep, 28 to 6,285 wide. A scheduler can be right on one topology and wrong on another.

5 topologies

Ordering is a property of the scheduler, not of the machine it ran on, so this result transfers to any deployment. Performance numbers do not: those are single-host and stated as such.

Porting took about twenty lines. WfFormat describes a DAG as tasks with parent references; so does Titan. The convergence was not planned, it is what the problem shape forces.

Landscape

The honest version, where Titan wins, and where it plainly does not.

Honest scoring.

Categories and capabilities, not raw quality. Airflow, Ray, Nomad and Temporal are mature systems with thousands of contributors. Some rows are lost on purpose.

This is not a replacement claim. Titan does not replace Kubernetes, Airflow, Ray, Nomad or Temporal, and nothing in this deck argues that it should. Kubernetes orchestrates containers; Titan schedules a job graph and manages processes, which is a different layer, and Titan runs perfectly happily inside a cluster Kubernetes is already managing. It also works the other way round: Titan can be the infrastructure, or it can be just the execution layer underneath something else. The Dagster integration in this repo has Dagster owning the asset graph, scheduling and lineage while Titan runs the work, via a TitanEngine resource of about thirty lines. The table below is here so you can see which parts overlap and which do not, and decide whether the overlap is relevant to you.

CapabilityTitanAirflowRayNomadTemporal
Dynamic DAG mutation at runtimeNativeLimitedYesNoYes
Tasks spawn tasks (agentic)NativeNoYesNoPossible
Services + batch in one runtimeYesNoYesYesPartial
Human-in-the-loop gatesNativeCustom sensorsNoNoSignals
Visual DAG builderNativeNoNoNoNo
Observability stack needed for scheduler internalsNone, in the boxStatsD/OTel + Prometheus + GrafanaDashboard built inMetrics endpoint + PrometheusPrometheus + Grafana
Dispatch loop broken down by phaseFive phasesOne critical-section metricNoCoarse eval/plan timingsNo
External deps to run coreNonePostgres, webserverHead nodeConsul (opt)Cassandra / SQL
Master HA / leader electionNo (v2)YesYesYesYes
mTLS / authNo (v2)YesYesYesYes
Production-readyResearch v1.0YesYesYesYes

Honest scope

The rows lost on the previous slide deserve more than a footnote.

What v1 is not.

ConstraintStatus
Master HASPOF, Raft leader election on v2
Transport securityUnencrypted TCP, mTLS on v2
IsolationProcess-level, Docker drivers on v2
Recurring schedulesDelayed execution today, cron on roadmap
NetworkAssumes a flat address space (LAN / VPC)
Service livenessProbed once at deploy, never re-probed
Scale-up cost~2 s of dispatch-loop time per spawned worker
Measured scale9,805 tasks in one submission, 5 workers / 20 slots, one host

The mechanisms above give durability and self-healing, no committed state or in-flight job is lost across a crash. What they do not give is availability during a Master outage.

The gap is availability-during-failure, not data loss. That distinction is the honest version of the claim, and it's the one worth making.

Run it inside a trusted VPC or over an SSH tunnel. Not multi-tenant, not internet-facing, not hard-SLA, by design, for now.

Measured, and separately, expected.
  • Every number in this deck comes from 60 jobs across 5 workers on one host. That is what was run, not a wall that was hit.
  • Nothing in the code structurally caps job or worker count. The queues are unbounded and workers self-register.
  • The known limit is throughput, not capacity. Dispatch is serialised at ~2 ms per job, so roughly 500 jobs/second however many workers you add, and every store write funnels through one synchronised adapter.
  • Medium scale is a fair read. A single 9,805-task graph runs correctly, but on one host: fleet size is the part that is not established.
  • Production load, multi-day runs and many-host deployments remain unvalidated.
  • Correctness is measured; capacity is not. Ordering has been verified against published workflows and transfers anywhere. The throughput figures are single-host and should be read as the floor of what has been demonstrated, not as a limit anyone has hit.

Status

Built solo.
Open source.

A research runtime engineered from first principles, currently v1.0. It handles complex distributed execution loops and agentic workflows, and it will find edge cases in non-standard environments. Contributions to harden the core are welcome.

Repository

github.com/ramn51/
titan-orchestrator

Documentation

ramn51.github.io/
titan-orchestrator

License

Apache 2.0 · Ram Narayanan A S

Visual Constructor shipped HITL shipped Raft · mTLS · Docker on v2

← → or space to navigate