Skip to content

Sous Functions Overview

Osvaldo edited this page May 19, 2026 · 1 revision

Sous Functions Overview

Sous is a separate project that runs alongside codeQ. It does not ship in this repository, it is not built by this repository's Makefile, and it does not appear in any of the codeQ binaries. The source, the CLI, the function-authoring guide, and the release artefacts all live at github.com/osvaldoandrade/sous. These wiki pages exist because Sous is the most common production consumer of codeQ, and the shape of the integration is worth describing from the codeQ side even though the integration's far side is documented elsewhere.

The Sous repository describes itself as follows. The wording is reproduced verbatim because it is the definition every other page in this section refers back to:

SOUS is a serverless execution layer for agent automation, deploying functions without compilation. The platform ensures runtime parity between local and cluster, executing functions in secure isolates.

The sentence carries four claims and each one constrains how Sous sits on top of codeQ. The execution layer is serverless in the sense that an operator does not provision a server per function: a function is a registered artefact that the Sous control plane schedules wherever a worker has capacity. Functions deploy without compilation because the Sous runtime hydrates them from source or from a packaged bundle into an isolate at invocation time. Runtime parity is the guarantee that the isolate semantics on a developer laptop match the isolate semantics on a cluster node, so that a function that runs locally also runs in production without surprises. Secure isolates are the unit in which a function actually executes — a process boundary with restricted syscalls and a resource cap on CPU, memory, and wall-clock time.

None of those four properties live in codeQ. codeQ has no notion of a function, an isolate, a control plane, or an invocation. It has tasks. The integration is the mapping from one set of words to the other, and that mapping is what the rest of this section describes.

Who Sous is for

The Sous audience is a team that wants to ship agent-built automation without operating its own scheduler. The agent in the project description is broad — it covers an LLM-driven assistant, a domain-specific generator, a workflow engine, anything that needs to invoke user-supplied logic without compiling and re-deploying the host. The shared property is that the logic to be executed is dynamic enough that pre-compiling it into the host process is not an option. Either the function comes from a third party, or it is generated on the fly, or it changes often enough that recompiling the host on every change is operationally expensive. Sous solves the dynamic-execution half of that problem; codeQ solves the durability-and-retry half. Together they offer a substrate where a function can be registered once, invoked many times, retried automatically on failure, and observed through the codeQ task lifecycle.

The audience is explicitly not someone looking for sub-millisecond RPC. An end-to-end Sous invocation is bounded from below by codeQ's claim latency plus the isolate startup time. That is in the tens of milliseconds in the best case and the hundreds in cold-start scenarios. For request-response work where every millisecond matters, the right answer is a long-lived process speaking gRPC, and Sous is not that. For medium-grained jobs that benefit from durability and retry — anything where "ran the function once" is more important than "ran the function in 800 microseconds" — Sous is a good fit, and the codeQ task model is what makes the fit work.

Why Sous depends on codeQ

A function execution layer needs four things from a backing system: a place to put pending invocations, a way to hand them out to executors without losing them, a record of what each invocation produced, and a retry policy for the executions that fail. Without those, every invocation has to be tracked by the executor itself, and the executor has to be either restartable in place or coordinated across replicas. Neither of those is easy. Both are exactly what codeQ already provides.

A pending invocation is a task in the PENDING state. A handed-out invocation is a task in IN_PROGRESS with a lease attached to one worker. A successful invocation is a task in COMPLETED with a ResultRecord attached. A failed invocation is a task in FAILED with an error string, or a task that exceeded MaxAttempts and landed in the dead-letter set. Sous does not need to write any of that; it gets it by registering its workers and its control plane against codeQ's wire surface. The Sous repository spends its complexity budget on the isolate runtime, on the function authoring contract, and on the control plane that ingests invocation requests. The scheduling and the durability come from the layer below.

That asymmetry is important. codeQ does not depend on Sous in any sense. It does not link against any Sous library, it does not know any Sous protobuf, it does not expose a "function" concept anywhere in its API. A codeQ deployment without Sous is a complete deployment. A codeQ deployment with Sous adds a sibling process tree — control plane, worker pool, CLI — that speaks to codeQ over the same producer and worker streams that any other application would use. The two systems are coupled at the wire level on :9092 and :9091, not at the code level.

How the wiki refers to Sous

These six pages treat Sous as a known but external system. They explain what codeQ does when Sous talks to it, which gRPC surface Sous touches, and which fields of a task carry Sous-level concepts like function name and serialized arguments. They do not document the Sous CLI in detail, they do not explain how to write a function, and they do not cover the isolate runtime's syscall table. Whenever those topics come up, the page links out to the Sous repository.

The pages are linked from the _Sidebar under the Sous heading and arranged in the order most newcomers will want to read them. Concepts is next; it is the longest page in the section and explains the wire model in detail. After that, Get Started walks through the integration topology, Configure Workers covers the worker-pool wiring, Deploy covers what happens when a function is registered with the Sous control plane, and Develop gives the brief contract a function author needs to keep in mind.

What Sous looks like from a codeQ operator's seat

If you operate codeQ and a team builds Sous on top of it, you will see three things appear in your monitoring. The first is a producer connection on :9092 from the Sous control plane — usually one connection per control-plane replica, occasionally more if Sous is configured for connection pooling. This connection carries every function invocation request as a CreateTask event. The Command field on each task is the function name and the Payload is the serialized argument list, but you do not need to know that to operate the queue: the task is an opaque unit of work and you size queue capacity, retention, and throughput the way you would for any other workload.

The second is a worker stream connection on :9091 from each Sous worker pool replica. These are bidirectional streams with the same shape as any other worker client built on pkg/workerclient. They issue Ready events, receive tasks or task batches, run the function in an isolate, and submit Result events. The lease semantics are exactly the same as for any other worker. If a Sous worker dies mid-task, the lease expires and the reaper requeues the task, and the next Ready from a surviving worker picks it up. There is nothing Sous-specific about that path.

The third is the result-read traffic. The Sous control plane reads completed task results via GetResult so it can return function output to whoever invoked the function. That traffic is a normal codeQ RPC and is bounded by the same cluster-routing rules as any other read. If the deployment is clustered, the read may be served by the local node or proxied to whichever node owns the shard. Either way the operator's view is just normal RPC volume on the gRPC port.

In all three cases the Sous-specific behaviour is invisible to codeQ. The operator metrics — task enqueue rate, claim latency, lease expirations, dead-letter depth — describe Sous's behaviour without needing a Sous-specific view. That is the consequence of layering Sous on the queue model rather than on a private control surface, and it is the same property that lets other applications run alongside Sous on the same codeQ instance without coordination.

Where the abstraction boundary sits

It is worth being precise about where Sous ends and codeQ begins, because the boundary is the load-bearing decision in this design. Everything to do with how a function gets from registration to a runnable artefact is Sous. That includes parsing the function source, bundling it into a runnable form, distributing it to workers, building an isolate, mapping arguments into the isolate's calling convention, capturing return values and panics, enforcing the resource cap, and tearing the isolate down afterwards. None of that touches codeQ, and codeQ would not know how to do any of it if it tried.

Everything to do with how an invocation persists, gets handed out, and gets retried is codeQ. That includes the FIFO ordering inside a priority bucket, the lease that gives one worker exclusive ownership of one task at a time, the heartbeat that lets a slow worker keep its lease, the reaper that returns abandoned tasks to the queue, the dead-letter set for tasks that exceed their attempt budget, the per-tenant token-bucket rate limiter, and the durable batch commit that persists each state transition before acknowledging it. Those mechanisms are described in detail elsewhere in this wiki — see Concepts Overview for the entry point — and they apply unchanged when the producer is Sous.

The boundary is the gRPC wire surface. On :9092 it is the producer stream that the Sous control plane uses to enqueue invocations. On :9091 it is the worker stream that the Sous worker pool uses to claim tasks and report results. Both surfaces are public, documented in the IO section, and stable across releases. A Sous worker pool that is built against the worker stream API does not depend on Sous internals on the codeQ side, and a codeQ release does not depend on Sous internals on its side. The two projects can evolve at their own pace as long as the protocol does not change underneath them, and the protocol is what these pages, the Sous repository, and the codeQ source tree all agree on.

What this page does not cover

This overview deliberately stops short of the integration mechanics. The wire-level mapping from a function invocation to a codeQ task lives on the Concepts page, which spells out which task field carries which Sous-level value, which result kinds map to which function outcomes, and how the lease lifecycle interacts with an isolate that runs longer than expected. The end-to-end runbook for a developer who has a function and wants to see it executed lives on Get Started, and it links out to the Sous repository for everything Sous-specific. The wiring for worker pools — how many slots per worker, how long a lease should be, when to enable batch claims — lives on Configure Workers.

Read this page once if you are evaluating whether Sous is the right shape for your workload. Read the rest of the section if you have decided it is, and operate codeQ in production. Read the Sous repository if you want to write a function. The three sets of pages are deliberately complementary and the boundaries are drawn at the points where one project's authority ends and the other's begins.

Clone this wiki locally