AppSurface Search
Guide

FirstDurableWorker

Source of truth

View source Edit this page

Last updated

This generated .NET 10 solution is an application-owned, public-preview example of a PostgreSQL-backed Durable Work host with an authenticated external wake. It contains no AppSurface source checkout or repository-test-project dependency. You own the generated host, Work contracts, authentication, deployment, and future upgrades.

The generated test project copies its xunit.runner.json beside the test assembly on every build. That file disables assembly and collection parallelism so the deadline, cleanup and real transport probes run with the reviewed scheduling policy. Keep the copy rule when changing the test project; merely retaining the JSON in source does not configure the test runner. See the generated-project upgrade checklist when updating an existing project.

The test builders also set hostBuilder:reloadConfigOnChange=false before ASP.NET Core creates its default configuration sources. These fixtures supply configuration explicitly and do not observe JSON file changes. Disabling reload avoids allocating native file watchers for every test host, which can stall builder construction in restricted environments before a startup deadline can run. In-memory configuration updates and the fixture's explicit reload scenarios remain available. Keep this setting on test builders; the generated application retains its normal configuration reload behavior. Real PostgreSQL and listener tests still require their documented host capabilities.

Blocking setup and cleanup adapters are invoked on dedicated threads before their returned tasks are observed. This keeps a synchronous Docker adapter from occupying the thread-pool workers needed for cancellation and deadline continuations. The dedicated thread ends when the adapter returns its task; the full asynchronous operation remains tracked. Pending counts reflect unfinished tasks independently of completion-bookkeeping scheduling. This isolation does not abort a blocked adapter or certify cleanup: pending work still withholds success, and all operations share the existing cleanup deadline. See the cleanup and recovery contract.

Install, create, and run the first proof

The template package version below is the authored coordinated baseline in this source tree. Use the exact compatible version published to your selected feed, retaining the prerelease suffix:

dotnet new install ForgeTrust.AppSurface.Durable.Templates@0.2.0-preview.13
dotnet new appsurface-durable-worker -n FirstDurableWorker
dotnet test FirstDurableWorker --filter FullyQualifiedName~FirstDurableWork --logger "console;verbosity=detailed"

The first command installs the native .NET template. The second creates this solution. The third runs the real PostgreSQL-backed proof. It requires the .NET 10 SDK and a running Docker daemon with Linux-container support. The test itself owns a disposable PostgreSQL 16.5 container, schema, role manifest, runtime epoch, SDK exporter, and cleanup; no manual database provisioning or host-installed psql is required for these commands. If Docker is unavailable, the test fails without emitting a successful checkpoint. Start Docker and rerun the third command.

Expected checkpoint lines are shown below. They are assertions required by the proof, not a transcript of a run already performed:

[first-work] authorized activation accepted
[first-work] Work reached terminal completion
[first-work] readiness: NotStarted -> Healthy
[first-work] exported appsurface.durable.runtime.activation

The proof distinguishes request acceptance, persisted terminal success, readiness, and actual SDK export. Completed means the pump call returned; it does not establish that every Work item succeeded. The test reads the persisted Work/result and flushes the SDK before it reports success.

After replacing the sample, validate the replacement in this order. These commands use FirstDurableWorker as the example generated project name; replace it with the name passed to dotnet new when it differs. In the unsubstituted template source tree, the corresponding project-name placeholder is AppSurfaceDurableWorker.

dotnet build FirstDurableWorker --warnaserror
dotnet test FirstDurableWorker --filter "Category!=PostgreSql&Category!=NativePostgreSql"
dotnet test FirstDurableWorker --filter FullyQualifiedName~FirstDurableWork --logger "console;verbosity=detailed"

The filtered contract run excludes both Docker PostgreSQL contracts and the harness-owned native PostgreSQL smoke. It does not replace the dedicated real Docker-backed FirstDurableWork proof in the third command above. The native smoke is a separate, intentionally fail-closed environment gate; when its required environment is absent, it fails rather than being skipped or relaxed.

For the canonical settings and API reference, recovery instructions, independent verification gates, and public-preview limits, see the Durable worker start guide. To replace the sample safely, follow docs/replace-sample.md.

The proof test namespace is fixed as DurableWorkerTemplate.Tests, independently of the generated application name. This keeps the documented FullyQualifiedName~FirstDurableWork filter limited to its Docker proof even when the project is named FirstDurableWorker; the separately configured native smoke is excluded. The friend-test assembly name still follows your application name.

Run the host separately

The test fixture provisions only its disposable proof database. A normal run of this host never creates schema or roles and never initializes an epoch.

Every built host gets a new privacy-safe template:<random GUID> worker identity for its claims and heartbeat. Concurrent hosts have independent identities even within one process; a restart creates a new identity. The identity is runtime ownership metadata, not a credential or a stable application identifier. Existing heartbeat retention remains operator-owned; see the Durable operations guide.

  1. Provision and verify a PostgreSQL database you control using the released schema, role, and epoch procedure. Follow the complete reviewed role manifest and the work_only dispatcher/runtime boundary.
  2. Configure the host with Durable:DispatcherConnectionString and Durable:RuntimeConnectionString using separate restricted identities, plus Durable:StoreId and the nonempty active Durable:RuntimeEpoch. The Durable:MaximumPoolSize, Durable:ConnectionTimeoutSeconds, and Durable:CommandTimeoutSeconds defaults are 10, 5, and 5; overrides must remain positive and bounded. These keys are in the generated appsettings.json. Keep migration-owner and retention credentials out of the host.
  3. For local development, run in Development and supply a fresh APPSURFACE_TEMPLATE_ACTIVATION_TOKEN through the process environment or a secret provider. This token has no default and is rejected outside Development. Production must configure a real ASP.NET authentication scheme and the named ActivationAuthorization policy; it must leave the sample token unset.
  4. Configure OpenTelemetry:OtlpEndpoint when an external OTLP collector is available. Its default is empty. The automated first proof uses an in-process SDK exporter and does not require a collector. The default listen URL is http://127.0.0.1:5080; configure an appropriate address for the environment.
  5. Start the app with dotnet run --project src/FirstDurableWorker/FirstDurableWorker.csproj. Check /live, /compatibility, and /ready independently. Stop the host before changing schema, grants, roles, or epoch.

WorkerHostSettings.ToString() prints only StoreId and RuntimeEpoch. It omits connection strings, tokens, role names, OTLP settings, and application values. Logging individual settings properties is still unsafe because they can expose sensitive configuration. The executable's catch boundary prints generic guidance for failures during host build, listener start, shutdown and disposal: provider and application code can throw the same exception types as host validation, so exception type alone cannot show that a message is safe to log. Builder creation and production authentication registration run before that catch boundary.

The proof's 5-second default body-read deadline is independent of the 2-second pump discovery and 10-second cooperative service request budgets. See the start guide for all accepted bounds, body semantics, health status rules, production ownership, and the difference between this Docker proof and the all-OS native PostgreSQL smoke. A successful template test is not a production deployment certificate.

For an already provisioned store, follow the Durable runtime doctor guide with a coordinated CLI/provider version that includes the command. Doctor performs read-only store/runtime checks; run your application's composition verifier afterward. Its clean result does not establish this proof's authorization, terminal Work, readiness transition, or exported activity.

Manual upgrades

Installing a newer template never changes this generated directory. Compare your application with the versioned generated shape v1 inventory and manual upgrade checklist. Preserve application-owned edits, merge reviewed host or configuration changes manually, and keep all direct AppSurface package versions coordinated.

Changes to Work name/version, codecs, persisted results, provider safety, or retry policy are application data and rollout decisions. Do not rewrite records already accepted under an earlier Work identity. Review the typed Work contract and Work protocol before changing them.