-
Notifications
You must be signed in to change notification settings - Fork 0
Integrations
Provenance. Generated by
/n8-mapfrom the commit named below, and not regenerated since. Individual statements may have been overtaken by later work — most of the issues this map cites have since been fixed and closed. Read it as a snapshot of the codebase's shape, not as a current defect list; the GitHub issue register is authoritative for what is actually open. (The generating commit hash predates a history rewrite and no longer resolves.)
Every external service and sidecar AutoNate.Web talks to — where the client lives, which config keys drive it, how it authenticates, what happens when it is down, and how to run it locally. Read Stack.md §8–§11 first for the commands and config keys.
Generated from commit 01f0f174 on 2026-08-31 by /n8-map.
browser ──5173 Vite──▶ AutoNate.Web :5108 ──HTTP Basic──▶ Flowable REST :8080 (JVM + flowable-extension jar)
│ │ ▲ │ daprd (flowable-dapr, netns-shared) ──▶ NATS JetStream :4222
│ ws://:1234 │ │ POST /api/workflow-behaviors/{key}/execute (X-AutoNate-Internal-Token)
▼ │ │
hocuspocus ──pg──▶ Postgres:5432 ◀── EF Core / Npgsql ── AutoNate.Web
│ POST /internal/yjs-auth, /internal/yjs-webhook (X-AutoNate-Internal-Token + HMAC)
▼
AutoNate.Web ──HTTP :3500──▶ daprd (autonate-web, host process) ──▶ NATS JetStream (pubsub) / Redis :6379 (statestore)
AutoNate.Web ──NATS request/reply──▶ services/executor (core queue group "executor"; no JetStream stream captures this subject — archived-141)
AutoNate.Web ──HTTPS──▶ api.anthropic.com / api.openai.com / api.tavily.com / REST data connectors
Rule of thumb enforced across the codebase: every outbound HttpClient is a named IHttpClientFactory client with an explicit Timeout, registered in Program.cs ("data-connector" 30 s at :490, "agent.anthropic"/"agent.openai" 5 min at :618-619, "agent.webfetch" 10 s + no cookies + 5 redirects at :645-654, "agent.websearch" 15 s + no redirects at :660-668, FlowableClient typed client 30 s at :935 via FlowableClient.ConfigureHttpClient). Probes that use the unnamed CreateClient() set httpClient.Timeout inline (2–3 s). Follow that pattern for any new integration; never new HttpClient().
-
Role: single source of truth for the app (
AutoNateDB), plus Flowable's own DB (flowable, the image'sPOSTGRES_DB), plus the optionalautonate_datastoresDB, plus per-plugin schemas. -
Client: EF Core 9 via
IDbContextFactory<AutoNateDbContext>(src/AutoNate.Web/Persistence/AutoNateDbContext.cs,.DataStores.cs,.ProjectionCaches.cs; scaffolded entities inPersistence/Scaffolded/). Registered atProgram.cs:265-280withDbConnectionFailureLoggingInterceptorandRelationalEventId.ConnectionErrorsuppressed (cancelled SPA requests otherwise spamfail: 20004). Stores areEfCore*Storeclasses underServices/<Domain>/; singletons that need DB access take the factory and create a context per call (e.g.Services/SystemIssues/EfCoreSystemIssueStore). -
Config:
ConnectionStrings:Default(dev:Host=localhost;Port=5432;Database=AutoNate;Username=autonate;Password=Your_password123!;Keepalive=30;Tcp Keepalive=true;Connection Idle Lifetime=60;Connection Pruning Interval=10). Keep the keepalive/pruning suffix when you copy the string — it exists to stop idle-pool drops. -
Schema init — no EF migrations:
Persistence/IDatabaseInitializer.csimplementations run inOrderat boot (Program.cs:1039-1048):PrimaryDatabaseInitializer(Order 0) →DatabaseSchemaInitializer.EnsureAsync(Persistence/DatabaseSchemaInitializer.cs:3746, ~70 sequentialExecuteSqlRawAsynccalls over idempotent SQL constants, 82CREATE TABLE IF NOT EXISTS), thenDatastoresDatabaseInitializer(Order 10). Base tables and the seededadminuser come frominfra/postgres/init/02-create-autonate-app-schema.sql(compose runs it once; the E2E fixture replays it). To add schema: append aprivate const string XyzSql = """…""";and anExecuteSqlRawAsync(XyzSql, …)line at the end ofEnsureAsync; write it idempotently (IF NOT EXISTS,ADD COLUMN IF NOT EXISTS,ON CONFLICT DO NOTHING). -
Datastores DB (
Services/DataStores/Sql/DatastoresDatabaseInitializer.cs): probespg_database,CREATE DATABASEvia the maintenance DBpostgres, then ensures theDataStores:Sql:WriterRole(autonate_datastore_writer) with a password fromDataStores:Sql:WriterRolePasswordor a generated one persisted at{Data:Root}/datastores-writer.secret. AbsentConnectionStrings:Datastores⇒ Info log, feature off, SqlType endpoints return 503.SqlDataStoreProvisionercreates one schema + read-only role per data store;CsvIngestorstreams viaNpgsqlBinaryImporter(COPY). -
Per-plugin isolation (
Plugins/PluginSchemaProvisioner.cs): 8-char code,LOGINrole with DataProtection-encrypted password (purposeAutoNate.Plugins.RolePassword.v1), schemaplg_<code>owned by that role, group roleplg_readersfor cross-plugin SELECT.Plugins/PluginMigrationRunner.csapplies<plugin>/migrations/*.sqlin lexical order, tracking in<schema>.__plugin_migrations, one transaction per file; a failure disables the plugin withlast_error. -
Failure behaviour: boot aborts if
ConnectionStrings:Defaultis missing (Program.cs:269) or the initializer SQL throws. Runtime connection faults are logged at Warning by the interceptor; request-cancellation errors are silenced. -
Local:
make infra-ensure(composepostgres:16-alpine, data ininfra/mounts/postgres/data, superuserautonate). Inspect:docker exec autonate-postgres psql -U autonate -d AutoNate. Tests createautonate_test_<guid>DBs (tests/AutoNate.Web.Tests/PostgresTestDatabase.cs); E2E recreatesAutoNate_E2E.
-
Client:
Services/Flowable/FlowableClient.cs(1560 lines) implementingIFlowableClient; typed HttpClient registered atProgram.cs:935-940.FlowableClient.ConfigureHttpClient(:1534-1549) setsBaseAddress = Flowable:BaseUrl + "/",Timeout = 30 s, andAuthorization: Basic base64(Username:Password). Every call doesawait EnsureSuccessAsync(response, "verb the thing")(:1515-1525) which throwsInvalidOperationException("Flowable could not <op>. HTTP <code> <reason>. <body>")— copy that wording when adding calls. Paths are relative (service/repository/deployments,service/runtime/process-instances,service/history/...,service/runtime/signals, …). -
Config:
Flowable:BaseUrl(http://localhost:8080/flowable-rest),Flowable:Username/Password(image defaultrest-admin/test).FlowableOptionsinConfiguration/InfrastructureOptions.cs. -
Script-task guard: before deploying BPMN containing a
scriptTask,EnsureJavaScriptScriptTaskSupportAsync(:1395-1435) probesactuator/scriptTaskSupportthenservice/autonate/script-task-support(both served by the extension) and refuses the deploy if the JVM has no JS engine or the probe endpoint is absent ("rebuild the Flowable image"). -
Projection cache:
Services/Flowable/Cache/Flowable{Execution,Task,Variable,History}PollingFeed.cspoll Flowable onFlowableCache:*PollIntervaltimers into Postgres cache tables through the projection framework (Program.cs:942-1003);FlowableReadThroughserves reads withReadThroughFreshness30 s;WorkflowCacheRetentionService+ColdTier/ColdTierArchiverService(Parquet + DuckDB,FlowableCache:ColdTier:Enabled=falseby default) age data out. -
Failure behaviour: exceptions propagate to endpoints (500 / problem details); polling feeds log and retry on the next tick.
SystemHealthService.CheckFlowableAsyncsurfaces reachability onGET /api/health/system. Tests replace the client withStubFlowableClient(AutoNateWebApplicationFactory.cs:100-104).
-
Registration:
FlowableExecutionEventAutoConfiguration(Spring Boot auto-config viaMETA-INF/spring/…AutoConfiguration.imports) registersWorkflowExecutionEventListener+WorkflowFailureEventListeneras Flowable engine event listeners, theautonateBehaviorDelegatebean (name is load-bearing — BPMN service tasks reference${autonateBehaviorDelegate}),dueDateHelper, and the script-task probe controller (GET /service/autonate/script-task-support) + actuator endpoint. -
Config (
FlowableExecutionEventProperties, prefixautonate.flowable-events, injected by compose asAUTONATE_FLOWABLE_EVENTS_*env vars):dapr-publish-base-url(http://127.0.0.1:3500— theflowable-daprsidecar sharing the container's network namespace),pubsub-name(pubsub),topic-root(workflow.execution.events),source-app-id(flowable),callback-base-url(http://host.docker.internal:5108),callback-shared-secret,behavior-timeout-seconds(30). -
Event loop: engine events (PROCESS_STARTED, ACTIVITY_, TASK_, PROCESS_COMPLETED/CANCELLED, failures) →
WorkflowExecutionEventMapper→DaprWorkflowEventPublisher.publish→POST {daprPublishBaseUrl}/v1.0/publish/{pubsub}/{topic}?metadata.rawPayload=true. Failures are logged at WARN and dropped (no retry). On the .NET sideDaprStreamingSubscriberalways subscribes toworkflow.execution.eventsand fans out throughBusWatcherStreamServicetoWorkflowSignalDispatcher,WorkflowExecutionErrorRecorder,WorkflowTaskNotificationListener, the/ws/bus-watcherWebSocket, and the SystemIssue detectors. -
Behavior callback loop:
AutoNateBehaviorDelegate.executereadsflowable:autonateServiceKind="behavior"+flowable:behaviorKeyoff the service task, POSTs execution id/variables to{callback-base-url}/api/workflow-behaviors/{key}/executewith headersX-AutoNate-Internal-Token: <secret>andX-Correlation-Id,timeout = behavior-timeout-seconds. Non-2xx / IO / timeout / missing config ⇒FlowableException(engine job retry); a JSON reply withfailed: trueis applied (variableUpdates) and not thrown so the workflow author branches on it. Server side:Endpoints/WorkflowBehaviorEndpoints.cs:22MapPost("/{key}/execute")on an.AllowAnonymous()group with.DisableAntiforgery().AddEndpointFilter<SharedSecretEndpointFilter>(); the filter (Endpoints/SharedSecretEndpointFilter.cs) does a constant-time compare againstWorkflowBehaviors:CallbackSharedSecretand returns 401 for missing header, mismatch, or unconfigured secret alike. Behaviors implementIWorkflowBehavior(Services/Workflow/Behaviors/, built-inUnlockAccountBehavior; plugins add more viaIPluginContext.Behaviors). -
Secret pairing:
.envAUTONATE_BEHAVIOR_CALLBACK_SECRET(compose → JVM) must equalWorkflowBehaviors:CallbackSharedSecret(.NET). Dev default on both sides:dev-only-workflow-behavior-secret-change-me. Non-Development .NET refuses to start without it (Program.cs:776-781). -
Local: image built by
infra/flowable/Dockerfile(Maven stage runsmvn --batch-mode test package);infra/ensure-up.shrebuilds on source change and force-recreatesflowable+flowable-dapr.FLOWABLE_PROCESS_HISTORY_LEVEL=fullis required for the variable-history UI. Health:curl http://127.0.0.1:8080/flowable-rest.
-
Sidecar:
autonate-webdaprd runs on the host, started bymake app(dapr run …) orinfra/start-autonate-web-sidecar.sh(daprd --app-id autonate-web --app-port 5108 --dapr-http-port 3500 --dapr-grpc-port 50001 --placement-host-address 127.0.0.1:50006 --scheduler-host-address 127.0.0.1:50007 --resources-path infra/mounts/dapr-dashboard/components). Components:infra/dapr/components/pubsub.yaml(pubsub.jetstream,natsURL nats://localhost:4222,streamName workflow-execution,deliverPolicy new, scopesautonate-web,flowable) andstatestore.yaml(state.redis,localhost:6379,actorStateStore true, scopeautonate-web). -
Config:
Dapr:*(Configuration/InfrastructureOptions.csDaprOptions):HttpEndpoint http://127.0.0.1:3500,GrpcEndpoint http://127.0.0.1:50001,PubSubName pubsub,StateStoreName statestore, placement/scheduler addresses (used only by the health page). -
Startup probe:
Services/Dapr/DaprSidecarProbe.cs—GET {HttpEndpoint}/v1.0/metadata, 2 s timeout, false on any error. In DevelopmentProgram.cs:1022-1037throws at boot unless it succeeds orAUTONATE_ALLOW_RUNNING_WITHOUT_DAPR=true. Outside Development nothing checks; publishes just fail. -
Publishing (outbound): never
DaprClient. All domain publishers (Services/Events/AuditEventPublisher.csDaprAuditEventPublisher,Services/Records/RecordEventPublisher.cs,Services/ApplicationEvents/ApplicationEventPublisher.cs,Services/Notifications/NotificationEventPublisher.cs) serialize an envelope and callIAuditEventOutbox.EnqueueAsync(topic, eventType, json). WithAuditOutbox:Enabled=true(default) that isEfCoreAuditEventOutbox→audit_outboxrow →Services/Events/AuditOutboxDispatcher.cs(BackgroundService, 2 s poll,FOR UPDATE SKIP LOCKED, exponential backoff 5 s→10 m, 50 attempts then parked; dead letters handled byAuditOutboxDeadLetterParkRemediator). With it false,DirectPublishAuditEventOutbox(Services/Events/AuditEventOutbox.cs:128) posts immediately. Both do:var publishUri = new Uri(endpoint, $"/v1.0/publish/{pubsub}/{topicEscaped}?metadata.rawPayload=true"); using var content = new ByteArrayContent(Encoding.UTF8.GetBytes(payloadJson)) { Headers = { ContentType = new("application/json") } }; await httpClientFactory.CreateClient().PostAsync(publishUri, content, ct);
Topics are
<root>.eventsconstants next to each publisher (record.events,application.events,notification.events,auth.events,iam.events,record-schema.events,site.events,workflow-admin.events,system.issues,agent.events,external-connections.events,content.events,dashboards.events,query.events,datastore.events,workflow.execution.events). A new top-level root must also be added toNatsStreamProvisioner.DesiredStreams(§4) or Dapr answers HTTP 500 "no response from stream" and the outbox loops. Use theadd-audit-event/add-record-event-typeskills. -
Subscribing (inbound):
Services/Signals/DaprStreamingSubscriber.cs(IHostedService) usesDapr.MessagingDaprPublishSubscribeClient.SubscribeAsync(gRPC/HTTP endpoints fromDapr:*,Program.cs:247-258). It always subscribesworkflow.execution.eventsand dynamically subscribes every signal-start topic registered by published workflows (IWorkflowSignalRegistry,EfCoreWorkflowSignalRegistry). There is no/dapr/subscribemanifest and no HTTP push endpoint. -
Watchdog: every 15 s the subscriber publishes
{}toapplication.healthprobe(DaprStreamingSubscriber.cs:312-338, 3 s timeout). Down→Up edge ⇒ dispose + resubscribe all handles. Unhealthy ≥ 45 s with ≥ 120 s since the last restart ⇒ it executesinfra/restart-autonate-web-sidecar.shvia/bin/bash(found by walking up ≤ 8 parents fromAppContext.BaseDirectory, 45 s wait) and re-syncs. This only works on a dev box with the repo layout; in containers the script is absent and the watchdog just logs. -
State store (Redis): only touched by
SystemHealthService.CheckRedisStateAsync(GET /v1.0/state/{StateStoreName}/health-probe, 3 s). No feature persists state through Dapr today. -
Health:
GET /api/health/dapr(probe) andGET /api/health/system(Endpoints/HealthEndpoints.cs) →Services/SystemHealth/SystemHealthService.csgraph: sidecar metadata, pub/sub publish probe, NATS stream stats, Redis via state API, Flowable, placement/scheduler TCP. -
Local:
make infra-ensurebrings updapr-placement:50006,dapr-scheduler:50007 (etcd data ininfra/mounts/dapr-scheduler/data),flowable-dapr;dapr init --slimonce sodaprdexists on the host (start-autonate-web-sidecar.shlooks inPATHthen~/.dapr/bin/daprd). Dashboard:make infra-up-dashboard→ :8081.
-
Client:
NATS.Client.JetStream2.5.10. Three usage patterns:-
Services/Nats/NatsStreamProvisioner.cs— short-lived connection at boot (Program.cs:1054-1058): deletes legacy streamautonate-records, thenCreateOrUpdateStreamAsyncforworkflow-execution(every<root>.>subject listed above,MaxAge 24 h) and(removed; listed inpipeline-code-runsLegacyStreamsToRemove) (pipeline-code-run.>, 24 h). Skipped with an Info log whenNats:Urlis empty. -
Services/Nats/INatsConnectionProvider.csNatsConnectionProvider— one lazily-opened shared connection for hot request/reply callers. -
SystemHealthService.CheckNatsAsync— short-lived connection readingworkflow-executionstream info (messages/consumers/lastSeq; zero consumers with messages = the silent Dapr disconnect signature).
-
-
Config:
Nats:Url(nats://127.0.0.1:4222in dev). No auth (compose runsnats --jetstream --store_dir /data --http_port 8222, data ininfra/mounts/nats/data). -
Executor request/reply:
Services/Pipelines/Execution/JetStreamCodeNodeRunner.csbuilds aCodeNodeRequest(CodeNodeWireFormat.cs, wireversion: 1,language js|python,kind transformer|analyzer,timeoutMs 30000,memoryMb 128) and calls an explicit inbox (NewInbox+SubscribeCoreAsync+PublishAsync(replyTo:)), skipping any inbox message that is not shaped like aCodeNodeReply(a JetStream PubAck would otherwise be parsed as a failed reply — archived-141) with a 30 s linked cancellation; timeout ⇒InvalidOperationException("Executor sidecar did not reply within 30s.").services/executor/src/index.tsis a core NATS subscriber (nc.subscribe("pipeline-code-run.>", { queue: "executor" }), not a JetStream durable consumer despite the comments) and replies{ success, errorMessage, output }on the reply subject; JS runs inisolated-vm(jsRunner.ts); Python runs in Pyodide inside a single-useworker_threadsWorker per request (pythonRunner.tsspawns/pools,pythonWorker.tsloads Pyodide with an emptyjsglobals, unregisterspyodide_js, disablesfetch/WebSocket; the parent enforcestimeoutMsvia SIGINT-in-interrupt-buffer thenterminate(), andmemoryMbvia aWebAssembly.Memory.prototype.growcap — archived-58/archived-161; knobsEXECUTOR_PY_WARM_WORKERS,EXECUTOR_PY_MAX_CONCURRENCY,EXECUTOR_PY_JS_HEAP_MB);isUnsafeis received but ignored. The executor runs as theexecutorservice ininfra/docker-compose.yml(started bymake infra-ensure; no ports,restart: unless-stopped); its compose healthcheck is a NATS request toexecutor.health(services/executor/src/healthcheck.ts), whichinfra/ensure-up.shwaits on via container health. -
Stream bootstrap outside the app:
infra/scripts/bootstrap-jetstream.sh(run by thenats-initcontainer on everycompose up) creates/editsworkflow-executionwith subjectsworkflow.execution.> record.> application.> content.>so the Flowable extension can publish before .NET boots.infra/ensure-nats-stream.sh(called at the end ofinfra/ensure-up.shand bymake infra-prepare) edits the same stream with--forceto onlyworkflow.execution.>— the app's provisioner re-widens it on the next boot, but betweenmake infra-ensureand app start, publishes to other roots fail. KeepNatsStreamProvisioner.DesiredStreamsandbootstrap-jetstream.shin sync, and prefer fixingensure-nats-stream.shto match if you touch it. -
Local:
make infra-ensure; monitor athttp://localhost:8222;docker run --rm --network container:autonate-nats natsio/nats-box:0.16.0 nats --server nats://127.0.0.1:4222 stream info workflow-execution.
- Used exclusively as the Dapr
statestorecomponent (infra/dapr/components/statestore.yaml,redis:7.4-alpine,--appendonly yes, data ininfra/mounts/redis/data, no password). No .NET code references Redis directly (grep -ri redis src/AutoNate.Web --include=*.cshits only the health service). The README lists it under Dapr pub/sub, but pub/sub is JetStream. -
Failure: the app keeps working;
GET /api/health/systemreports therediscomponent Down via the Dapr state probe. -
Local: compose service
redis:6379;docker exec autonate-redis redis-cli ping.
-
Sidecar:
services/hocuspocus/src/index.ts—@hocuspocus/serveronHOCUSPOCUS_PORT(1234) with three extensions:persistence.ts(Y.Doc binary state in the AutoNate DB tableyjs_documents, created byDatabaseSchemaInitializerYjsDocumentsSchemaSqlat:2952; seeds first-open docs frompages.body_jsonb/notes.content_jsonb),auth.ts(calls .NET),webhook.ts(mirrors snapshots to .NET). Document names are<prefix>:<guid>with prefixespage,note,napkin,diagram,documents(materializers.ts:287-293). Required env:YJS_INTERNAL_SHARED_SECRET,AUTONATE_WEB_URL,POSTGRES_DB/USER/PASSWORD(process.exit(1)if missing);unhandledRejection/uncaughtExceptionare logged, not fatal. -
.NET side (
Endpoints/YjsEndpoints.cs, optionsServices/Yjs/YjsServerOptions.cs):-
POST /api/yjs/ticket(cookie-authenticated,:44-46): authorizes the caller on the document viaIContentAuthorizer, mints an HMAC-SHA256 ticket (MintTicket:629, payload = document, actor, display name, roleeditor|commenter|viewer, jti, exp =TicketTtlSeconds60) and returns{ ticket, wsUrl: HocuspocusWsUrl, expiresInSeconds, role }. The SPA passes the ticket as theHocuspocusProvidertoken. -
POST /internal/yjs-auth(:219, filterYjsInternalSecretEndpointFilteron headerX-AutoNate-Internal-Token): verifies the HMAC, document-name match, single-use jti viaIMemoryCache(yjs-jti:<jti>, TTL+60 s — single-instance only), re-runs the authorizer, returns{ userId, displayName, role }.auth.tssetsconnectionConfig.readOnly = truefor any role other thaneditor. -
POST /internal/yjs-webhook(:344, same filter plusX-AutoNate-Yjs-Signature: sha256=<hex HMAC of raw body>check): stores the materializedbodyJsonbverbatim into the page/note/document row.YjsManagedContentGuard(Endpoints/YjsManagedContentGuard.cs) rejects REST patches to Yjs-managed bodies — write through Yjs, never through the REST body.
-
-
Secret pairing:
.envYJS_INTERNAL_SHARED_SECRET(compose → sidecar) must equalYjsServer:InternalSharedSecret(.NET). Dev defaultdev-only-yjs-internal-secret-change-me; non-Development .NET refuses to start without it (Program.cs:790-795). -
Failure behaviour: sidecar down ⇒ browsers cannot connect (ticket still issues); .NET down ⇒ every WebSocket connect is refused (auth hook throws) and webhook
changeposts throw (Hocuspocus logs, next edit retries — Y.Doc state in Postgres is authoritative, the JSON mirror lags);disconnectwebhook failures are swallowed. No/healthendpoint —ensure-up.shtreats an open TCP port as ready. -
Local: compose service
hocuspocus(restart: unless-stopped,AUTONATE_WEB_URL=http://host.docker.internal:5108,extra_hosts host-gatewayfor Linux). Rebuild is automatic on source change viaensure-up.sh;--forceVite deps after heavy installs.
-
Storage: admin-managed rows in
external_connectionsviaServices/ExternalConnections/EfCoreExternalConnectionStore.cs; secrets encrypted with ASP.NET DataProtection (DataProtectionConnectionSecretProtector.cs, purposeAutoNate.ExternalConnections.v1; keyring under the content root by default — mount/persist it or rotated keys make stored secrets unreadable).RevealForResolverAsyncis the only decrypt path. Kinds are strings:LlmProvider:Anthropic,LlmProvider:OpenAI,WebSearchProvider:Tavily. Metadata JSON may carrymodelandbaseUrloverrides.baseUrlis allowlisted per kind byServices/ExternalConnections/ProviderBaseUrlPolicy.cs(IProviderBaseUrlPolicy) at the three places untrusted metadata becomes a destination —ChatProviderResolver,WebSearchProviderResolver,ConnectionModelLister— because the decrypted key rides on every request built from it (archived-61). Built-insapi.anthropic.com/api.openai.com/api.tavily.com; extend viaExternalConnections:AllowedProviderHosts:<kind>(host-only,*.wildcard allowed); https required. -
Chat:
Services/Agent/Providers/ChatProviderResolver.cspicks the connection (explicit id or default-enabled for the kind), resolves the model (metadata.model→IAgentModelCatalogdefault/first-available → hard-codedclaude-sonnet-4-6/gpt-4.1) and buildsAnthropicChatProvider(POST {baseUrl|https://api.anthropic.com}/v1/messages, headersx-api-key,anthropic-version: 2023-06-01, SSE streaming) orOpenAIChatProvider({baseUrl|https://api.openai.com},Authorization: Bearer) over the 5-minute named clients. Non-2xx becomes aChatStreamChunk.Error(retryable when ≥ 500) rather than an exception.ConnectionModelLister.cslists models from/v1/modelson both. Model catalog seed + default live inagent_model(DatabaseSchemaInitializerAgentModelCatalogSeedSql), broadcast to SPAs over/ws/agent-model-default. -
Search / fetch:
Services/Agent/Search/WebSearchProviderResolver.cs→TavilyWebSearchProvider(https://api.tavily.com,"agent.websearch"client).WebFetchSkilluses"agent.webfetch"withIDnsResolverfor SSRF checks. Both are filtered out of the tool list when the site settingchatbot.internetAccessEnabledis off. -
Loop bounds:
Agent:MaxIterations25,Agent:ToolTimeoutSeconds30,Agent:DefaultMaxTokens4096 (Services/Agent/Loop/AgentOptions.cs). Streams to the browser via SSE on/api/agent/...— proxies must not buffer. -
Failure behaviour: no enabled connection ⇒
ResolveDefaultForKindAsyncreturns null and the chat endpoint reports a configuration error; provider errors surface as error chunks in the stream. Tests stub HTTP (AnthropicChatProviderTests.cs,OpenAIChatProviderTests.cs). -
Local: create a connection at the External Connections admin page (or
POST /api/external-connections) with a real API key; nothing in compose.
-
Contract:
Services/DataConnectors/IDataConnectorHandler.cs—Kind,TestAsync(DataConnector),FetchAsync(connector, state, IConnectorFetchSink). RegistryDataConnectorHandlerRegistrycomposes host handlers + plugin ones (Plugins/PluginConnectorRegistry→PluginDataConnectorAdapter.cs). -
REST (
Builtin/RestDataConnectorHandler.cs): GET only, auth modesbearer/basic/api-keyfromRestConnectorConfig,{lastFetchDate}URL interpolation, JSONRowsPathselection, uses the"data-connector"client (30 s). The interpolated URL goes throughServices/Http/OutboundUrlGuard.cs(IOutboundUrlGuard) before any socket opens — scheme must be http/https (https required outside Development) and every resolved address must be public, so loopback / RFC1918 / link-local169.254.169.254/ CGNAT / IPv6 ULA destinations are refused (archived-60). No host allowlist here on purpose: calling arbitrary third-party APIs is the feature. NoteParseConfigbinds case-sensitively while the SPA writes camelCase, so UI-authored configs currently do not bind (archived-165).TestAsyncreturnsConnectorTestResult.Fail(...)on HTTP/network/config errors instead of throwing. -
SMB (
Builtin/SmbDataConnectorHandler.cs): registered so the kind appears in the UI, butTestAsyncfails with an explanatory message andFetchAsyncthrowsNotSupportedException— not implemented. -
Consumers: Datasets (
Services/Datasets/*,DatasetRefreshScheduler1-minute cron loop,CachedDatasetMaterializer) and Pipelines (Services/Pipelines/Orchestration/PipelineRunWorker5 s poll). Files-backed datasets parse viaServices/Datasets/Files/{CsvFileParser,RawFileParser}from{Data:Root}/datastores.
-
Load path:
Plugins/PluginHostedService.csat boot sweeps orphan folders underPluginRuntime.PluginRoot(Plugins:Folderor{Data:Root}/plugins), hard-deletesDeletedPendingrows, then loads everyEnabledrow throughPlugins/PluginRuntime.csEnableAsync: extract zip → validateplugin.json(PluginManifest.cs:name,version,entryAssembly, optionalentryType,templates) → provision schema/role (PluginSchemaProvisioner) → runmigrations/*.sql(PluginMigrationRunner) → new collectiblePluginAssemblyLoadContext(Plugins/PluginAssemblyLoadContext.cs;SharedAssemblies= Abstractions, DI/Logging Abstractions, Npgsql, Dapper resolve to the host ALC) → instantiateIAutoNatePlugin→Configure(IPluginContext)(hooks viaIHookRegistrar, menus, behaviors, connectors, transformers, analyzers, agent skills, scheduled projections, page templates). Failure flips the row toDisabledwithlast_error; withPlugins:FailFastOnStartup=truethe host exits instead.DisableAsync/CleanupAsynccallCleanup(context)thenalc.Unload(). -
Upload:
POST /api/admin/plugins(Endpoints/AdminPluginsEndpoints.cs) validates size (Plugins:MaxUploadBytes) and zip contents (PluginUploadValidator.cs). Plugins run in-process with the host's identity; there is no sandbox beyond the ALC and the per-plugin DB role. -
Hooks:
Hooks/(HookRegistrar,IActionHub,IFilterHub;HookPointsconstants in the abstractions project). Host services dispatch throughIActionHub/IFilterHub; plugins receiveIHookRegistrar. Data access:IPluginDataAccessreturns hostNpgsqlConnections scoped to the plugin role. -
Local: build any project under
plugins/→dist/<Name>.zip→ upload + enable at/admin/plugins. Sample plugins:plugins/HelloPlugin(minimal),plugins/Auditor(migrations + page templates + audit firehose). The test plugintests/AutoNate.Web.Tests.SamplePluginis loaded from disk byPluginLoaderTests.
-
Vite dev proxy (
src/AutoNate.Spa/vite.config.ts): forwards/api,/account,/dapr,/bus-watcher,/filesand WebSockets/ws/bus-watcher,/ws/agent-model-defaulttoASPNETCORE_URL ?? http://localhost:5108; swallowsECONNRESET/EPIPEWS-proxy noise. SpaProxy (Microsoft.AspNetCore.SpaProxy10.0.7) spawns it from thehttplaunch profile. -
MCP servers (
.mcp.json):mantine(npx -y @mantine/mcp-server) for component docs,playwright(npx @playwright/mcp@latest --output-dir ./temp). -
Dependabot (
.github/dependabot.yml) tracks nuget (root), npm (/src/AutoNate.Spa,/services/hocuspocus,/services/executor, root), maven (/flowable-extension), and github-actions. CI lives in.github/workflows/ci.yml(added after this map was generated).
Getting started
Using Auton8
- Records
- Workflows
- Documents-and-Notes
- Queries-and-Dashboards
- Data-Stores-and-Pipelines
- The-Assistant
- Administration
Building Auton8
Repository