Skip to content

External Services

npond edited this page Sep 2, 2026 · 1 revision

External services

Auton8 is one ASP.NET Core application, but it does not work alone. This page is the honest inventory: what runs beside it, what each thing is actually responsible for, and what happens when it is missing.

Integrations has the wiring detail — environment variables, component files, connection strings. This page is the why.

The dependency surface, at a glance

Service Required? Owns Without it
PostgreSQL 16 Yes All application data, and Flowable's own tables Nothing runs
Flowable For workflows BPMN execution, tasks, process state Workflow studio and executions fail; the rest of the app works
Dapr For events Pub/sub, state, service invocation Bus Watcher and event-driven features go quiet; app refuses to start in Development unless overridden
NATS + JetStream For streams Audit events, workflow signals, executor job queue Streams unavailable; provisioner asserts them on boot
Redis Indirect Dapr's state store and pub/sub backend Dapr features degrade
Hocuspocus (Node) For collaboration Yjs document sync for Notes and Documents Editors fail to connect; specs fail on console errors
Executor (Node) For pipelines Sandboxed user code (transformers, analyzers) Code-transformer pipeline steps cannot run
An LLM provider For the assistant Model inference The chatbot cannot answer; everything else is unaffected

That is nine containers for a single-host application, and it is a real cost — tracked openly as a design concern in Concerns.

PostgreSQL

The system of record. It holds the application schema, the autonate_datastores schema used by SQL-backed data stores, and — in the default Compose layout — Flowable's tables in a separate flowable database on the same instance.

Two things worth knowing:

  • Schema is initialised by the application at startup, not by migrations. DatabaseSchemaInitializer runs idempotent DDL and seed blocks, each guarded by IF NOT EXISTS or a marker row in auth_seed_state. That is why upgrading is usually "start the new build".
  • Roles are cluster-wide. Plugin data isolation creates a plg_readers role and per-plugin plg_<code> roles. Creating a role cannot be done with check-then-act — two hosts starting together will collide on pg_authid_rolname_index — so those statements catch duplicate_object and unique_violation instead.

Flowable

The BPMN engine. Auton8 models processes, deploys them to Flowable, and reads execution state back; Flowable owns the runtime.

The integration is two-way:

  • Auton8 → Flowable over REST, for deploy, start, task completion, and queries.
  • Flowable → Auton8 over an HTTP callback, for service tasks. BPMN service tasks are routed through a fixed Flowable bean using the delegate expression ${autonateBehaviorDelegate}, in the namespace http://autonate.dev/workflows.

That callback is authenticated with a shared secretWorkflowBehaviors:CallbackSharedSecret on the Auton8 side, matching autonate.flowable-events.callback-shared-secret on Flowable's. Outside Development the host refuses to start without it.

The custom Flowable image adds the PostgreSQL JDBC driver so it can use Postgres rather than its in-memory default.

Dapr

Used for pub/sub, state, and the scheduler. It is deliberately not part of Compose: it is app-scoped and should start and stop with the app process, which is why make app launches it alongside the host.

The control-plane pieces (dapr-placement, dapr-scheduler) are in Compose, because they are shared and long-lived. Component YAML is tracked in infra/dapr/components and mirrored into the dashboard's mount.

In Development the app fails fast when no sidecar is reachable. Set AUTONATE_ALLOW_RUNNING_WITHOUT_DAPR=true to bypass that deliberately.

NATS with JetStream

The durable stream transport: audit events, workflow signals, and the pipeline-code-run.> subject the executor consumes.

JetStream must be explicitly enabled — NATS only turns it on when started with --jetstream, which is why CI starts it with docker run rather than as a service container (service containers cannot pass a command). The app's NatsStreamProvisioner asserts the streams it needs on boot.

NATS runs with no authentication in the local stack, which is precisely why its ports are bound to loopback: publishing them host-wide would let anyone reaching the machine queue executor work, subscribe to audit events, and read JetStream state.

Redis

Backs Dapr's state store and pub/sub. Auton8 does not talk to Redis directly — it is an implementation detail of the Dapr components. That indirection is noted in Concerns as a candidate for simplification.

Hocuspocus — the collaboration sidecar

services/hocuspocus, a Node service on port 1234. It is the Yjs sync server behind the Notes and Documents editors: multiple people editing the same document, with presence and cursors.

It authenticates against Auton8 rather than trusting clients: the SPA obtains a signed ticket, and the sidecar verifies it via a webhook back to the app, carrying an HMAC signature and the X-AutoNate-Internal-Token header derived from YJS_INTERNAL_SHARED_SECRET. The sidecar reads persistence from Postgres itself.

Executor — the sandboxed code runner

services/executor, a Node service with no ports. It consumes pipeline-code-run.> from NATS and runs user-authored transformer and analyzer code — JavaScript in isolated-vm, Python via Pyodide.

This is the one component whose whole job is to run untrusted input, so its isolation has been the subject of two critical findings (both fixed): the Pyodide js module exposing the Node host, and a shared interpreter with a timeout that could not fire. Treat changes here as security-sensitive.

LLM providers

The in-app assistant talks to a model you configure — there is no bundled model and no default endpoint. Providers are configured as external connections, and the model catalogue is administered separately, so the agent's context-window handling matches the model actually in use.

Provider API keys are encrypted at rest with ASP.NET Core Data Protection under the purpose string AutoNate.ExternalConnections.v1. That string is load-bearing: renaming it makes every stored secret undecryptable.

Outbound requests are constrained by an allow-list of hosts per connection kind, because the stored key is sent to whatever baseUrl names.

A note on naming

Internally these are wired with AUTONATE_* environment variables, X-AutoNate-* headers, an autonate_datastores schema, and an autonate.web event source id. The product is Auton8; the code is AutoNate. That split is deliberate — see the naming section in CONTRIBUTING.

Clone this wiki locally