# Native Windows Deployment — IIS and Worker

Sibyla v3.2 runs without Docker, WSL, or Redis:

- `Sibyla.Api` in IIS
- `Sibyla.Web` in IIS
- `Sibyla.Worker` as a Windows Service
- PostgreSQL installed natively
- existing Hermes Gateway started by Windows Task Scheduler

## Prerequisites

- Windows Server or the designated Windows pilot host
- .NET 10 Hosting Bundle for IIS
- .NET 10 Runtime for the Worker
- PostgreSQL with a dedicated Sibyla database and least-privilege application role
- IIS certificates and bindings for the API and Web hosts
- access to the existing IdentityServer authority
- a restricted local account for Hermes document extraction

## Publish

From the repository root:

```powershell
.\scripts\publish.ps1 -Configuration Release
```

Artifacts are written to:

- `artifacts\publish\Sibyla.Api`
- `artifacts\publish\Sibyla.Web`
- `artifacts\publish\Sibyla.Worker`
- `artifacts\publish\Sibyla.PdfTextExtractor`

## Required configuration

Set production values through environment variables or an approved Windows secret
store. Do not place passwords or refresh tokens in repository configuration.

Required API values:

- `ConnectionStrings__SibylaDb`
- `Sibyla__PublicBaseUrl`
- `Identity__Authority`
- `Identity__Audience`

Required Web values:

- `ConnectionStrings__SibylaDb`
- `Sibyla__PublicBaseUrl`
- `Identity__Authority`
- `Identity__ClientId`
- `Identity__ClientSecret`

Required Worker value:

- `ConnectionStrings__SibylaDb`

Provision `sibyla.document-log.read` and `sibyla.document-log.write` as dedicated
API scopes for approved service clients. Interactive Finance, Accounting, and
Administrator users continue to use role authorization; do not grant those broad
roles to an agent merely so it can read or write document comments.

Provision `sibyla.channel-intake.write` as a dedicated API scope and create a
confidential service client specifically for Apolo with client-credentials grant
access to only that scope. Store its secret in the approved Hermes secret store or
process environment. Do not reuse the interactive Web client, the general API
service-client credential, an administrator identity, or the Document Log scopes.
The scope itself remains associated with the existing `sibyla-api` API resource so
issued tokens carry the configured Sibyla API audience.
Apolo obtains tokens from `{Identity__Authority}/connect/token` and calls only the
versioned `/api/channel-intake/v1` endpoints documented in
`docs/hermes-apolo-document-intake-addendum.md`.

When Nextcloud is selected:

- `DocumentStorage__Provider=Nextcloud`
- `DocumentStorage__Nextcloud__BaseUrl`
- `DocumentStorage__Nextcloud__UserName`
- `DocumentStorage__Nextcloud__AppPassword`
- `DocumentStorage__Nextcloud__RootPath`

Job controls:

- `Jobs__PollInterval`
- `Jobs__LeaseDuration`
- `Jobs__HeartbeatInterval`
- `Jobs__InitialRetryDelay`
- `Jobs__MaximumRetryDelay`
- `Jobs__ExtractionConcurrency`
- `Jobs__AssessmentConcurrency`
- `Jobs__IntegrationConcurrency=1`
- `Jobs__MaintenanceConcurrency`

Restricted Hermes extraction:

- `HermesExtraction__ExecutablePath`
- `HermesExtraction__AgentHome=C:\SibylaApps\Documental-agent`
- `HermesExtraction__Profile=documental-agent`
- `HermesExtraction__WorkingRoot` (an ACL-restricted per-job staging parent)
- `HermesExtraction__Timeout`

Restricted Hermes financial decision support:

- `HermesFinancialAgent__Enabled=false` until target-host acceptance passes
- `HermesFinancialAgent__AutomaticDocumentAssessment=true`
- `HermesFinancialAgent__ExecutablePath`
- `HermesFinancialAgent__AgentHome=C:\SibylaApps\Financial-agent`
- `HermesFinancialAgent__Profile=financial-agent`
- `HermesFinancialAgent__WorkingRoot` (a separate ACL-restricted staging parent)
- `HermesFinancialAgent__Timeout`

Set `HermesFinancialAgent__Enabled` consistently for API and Worker. The API uses it
to permit manual queue requests; only the Worker receives the executable, profile
home, and staging paths. The profile consumes normalized JSON and returns proposals;
it receives no Cegid, Graph, database, storage, or channel credentials.

Cegid is disabled by default in all three hosts. Deploy the binaries and database
migration with `Cegid__Enabled=false`; enabling it is a separate, staged operation
described in `docs/cegid-integration.md`. Provision one shared Data Protection key
ring and a backed-up `LocalMachine\\My` certificate whose private key is readable
only by the API, Web, and Worker identities. Configure only the key-ring path,
certificate thumbprint, and global Cegid switches on the hosts. Administrators enter
each company's HTTPS endpoints, client credentials, fiscal-year mappings, and
payment defaults in **Empresas internas**; Sibyla stores the secret as company-bound
ciphertext in PostgreSQL. Companies without a tested, enabled connection are skipped
without recurring jobs or configuration errors.

The financial workbook projection remains independently controlled by
`ExcelCommit__MicrosoftGraph__Enabled`. Do not enable it until the workbook copy has
the required `MST_ID` and `CegidProductCode` columns, all three table fingerprints
have been recorded, and the delegated DPAPI token cache has been initialized under
the Worker service identity.

Startup validation rejects a production host with missing required values or an
integration concurrency other than one.

## PostgreSQL

Create a dedicated database and application role. The application applies EF Core
migrations at startup, so the deployment identity currently requires schema-change
rights. Split migration and runtime identities before production hardening if the
organization requires stricter separation.

The initial migration creates an append-only trigger on `AuditEvents`. The
application DbContext independently rejects updates and deletes to the same table.

The Cegid release adds the `CegidIntegration`, `FinancialAgentAssessments`, and
additive `PerCompanyCegidConfiguration` migrations. They create the canonical
purchase/sales integration, assessment, encrypted-connection, and fiscal-year
mapping tables and indexes. Review the
idempotent SQL included in the deployment handoff and take a restoreable PostgreSQL
backup immediately before starting the new API, which applies pending migrations.

Back up PostgreSQL off-host. A restore into an empty environment is an F3 release
gate.

## IIS

Create separate application pools and sites for API and Web:

- use `No Managed Code`
- use dedicated least-privilege application-pool identities
- grant write access only to each application's log directory
- terminate HTTPS in IIS
- configure environment variables at the application-pool or site level
- do not grant the Web pool direct access to document storage secrets when the API boundary can be used instead

Verify:

- API `GET /health/live`
- API `GET /health/ready`
- API `GET /api/system/info`
- Web OIDC login and logout

## Worker service

Install the published Worker under a dedicated least-privilege service account.
The service account requires database access, its log directory, and only the
external resources needed by registered deterministic job handlers.

Example installation from an elevated PowerShell prompt:

```powershell
New-Service `
  -Name "GottSibylaWorker" `
  -BinaryPathName "D:\Apps\Sibyla\Worker\Sibyla.Worker.exe" `
  -DisplayName "GOTT Sibyla Worker" `
  -StartupType Automatic

Start-Service GottSibylaWorker
```

The Worker starts extraction and proposal-only assessment loops at configured
concurrency, exactly one integration loop, and maintenance loops. A process stop does not return claimed
jobs immediately; another worker may reclaim them only after their lease expires.

The extraction handler stages one immutable original in a job-specific directory,
starts the canonical `documental-agent` profile without a shell, replaces the child
environment with a small allowlist, bounds stdout/stderr, terminates the complete
process tree on timeout, validates `sibyla.extraction.v3.1`, and removes staging data.
On Windows, `SYSTEMROOT` is copied from the parent environment; `WINDIR`, `PATH`, and
unrelated ambient variables remain absent.
For readable PDFs, the Worker also creates a bounded UTF-8 text-layer evidence file
only inside the transient job root and invokes Hermes with the repository-versioned
`sibyla_document_evidence` and `vision` toolsets. The prompt and command line contain
no extracted document content. The plugin verifies the fixed path, SHA-256, size,
encoding, truncation flag, and link/reparse protections before returning line-prefixed
untrusted data. Visual-only PDF fallback and PNG/JPEG processing receive only the
`vision` toolset.

Install `Sibyla.PdfTextExtractor` as its own narrow executable; the example uses
`C:\SibylaApps\Sibyla.PdfTextExtractor\Sibyla.PdfTextExtractor.exe`. Set
`HermesExtraction__PdfTextExtractorExecutablePath` to it. The Worker launches it
directly without a shell or inherited environment and applies the configured hard
timeout (15 seconds by default). Failure, timeout, malformed control output, or an
invalid evidence file fails closed to vision. On Windows, a kill-on-close Job Object
also limits the helper to one process and 256 MiB of process memory when assignment
is permitted; timeout or cancellation terminates the entire process tree. Grant the
Worker identity read/execute, but not write, access to the helper deployment.
The configured helper path must be fully qualified, must resolve to an existing
local path (UNC paths are rejected on Windows), and is checked before launch. This
pathname check does not bind an already-open file identity through `CreateProcessW`:
deployment ACLs must prevent the Worker or other untrusted principals from replacing
the executable between validation and launch, and mapped-drive locality must be
controlled by target-host policy.

On Windows, the helper is launched with a token derived from the Worker token using
`CreateRestrictedToken(DISABLE_MAX_PRIVILEGE)` and `CreateProcessAsUser`. This
privilege reduction is defense in depth only: it retains the Worker's SIDs and
integrity level and does not itself restrict network access or access allowed by
existing ACLs. The suspended, fail-closed Job launch is process containment, not a
parser security boundary. Production deployment remains blocked until the complete
boundary is target-host validated, using a dedicated service identity or another
reviewed design with no network, application, database, or secret access and only
helper read/execute plus transient current-job-directory access.

`scripts\publish.ps1` packages the manifest and runtime source as the versioned
`Sibyla.DocumentEvidencePlugin\1.0.0` release artifact, with `SHA256SUMS.txt`; tests,
caches, and runtime data are excluded. The version target is cleaned and recreated,
and publishing requires the exact four expected top-level relative paths with no
subdirectories or stale files. Publishing fails if the helper or required plugin
files are absent or if copied hashes differ. During target-host release,
verify the recorded hashes and copy that versioned artifact to exactly:

`C:\SibylaApps\Documental-agent\profiles\documental-agent\plugins\sibyla-document-evidence`

Then verify its registration from the deployed profile:

```powershell
& 'C:\SibylaApps\Documental-agent\.venv\Scripts\hermes.exe' -p documental-agent tools list
```

The listing can confirm that `sibyla_document_evidence_read` is registered, but it
does not prove that the plugin can read content. Live functional verification must
run under the restricted identity with the fixed `SIBYLA_DOCUMENT_JOB_ROOT`,
`SIBYLA_DOCUMENT_EVIDENCE_PATH`, `SIBYLA_DOCUMENT_EVIDENCE_SHA256`, and
`SIBYLA_DOCUMENT_EVIDENCE_TRUNCATED` environment, then make a fresh `--oneshot` call
with the fixed `sibyla_document_evidence,vision` toolsets and an instruction to call
`sibyla_document_evidence_read` exactly once. The target-host helper and plugin
deployment, restricted-identity probes, and a readable-document end-to-end flow
have passed for the integrated prototype. The sanitized result and artifact hashes
are recorded in `docs/deployment/documental-textlayer-v31-evidence.md`.

This prototype acceptance is not production acceptance. The restricted-token
launch reduces enabled privileges but does not satisfy or validate the complete
boundary, so the production security blocker above remains in force.
Deploy the Worker under the restricted identity accepted by F0 and keep database,
storage, and channel credentials out of that identity's profile files. Production
startup fails when the Hermes executable, home, profile, or staging root is absent.

When enabled, the assessment handler separately stages one normalized
`sibyla.financial-context.v1` JSON file, invokes `financial-agent`, validates strict
`sibyla.financial-assessment.v2`, stores the result immutably, and removes staging
data. Assessment jobs use their own lane and never gate or execute integration jobs.
Keep the feature disabled until the profile-specific security and reboot checks in
`docs/hermes-financial-agent-instructions.md` pass.

## Hermes Gateway

Hermes Gateway already owns email, WhatsApp/Baileys, and Mattermost. Deploy Sibyla
without duplicating these adapters. Configure the existing `orquestrador` profile
to call the Sibyla API when the F2 intake contract is available.

Use Task Scheduler with "Run whether user is logged on or not" and restore the
existing `HERMES_HOME` before starting the gateway. Reboot, Baileys-session, and
restricted-`documental-agent` verification remain explicit F0 acceptance tests.

The authoritative Hermes profiles are hosted on the AWS Windows Server 2025
Datacenter target,
not on development workstations. Deploy
`docs/hermes-documental-agent-instructions.md` as persistent instructions
for the existing `documental-agent` profile. Provision `financial-agent` separately
using `docs/hermes-financial-agent-instructions.md`; do not reuse the documental or
orchestrator profile home, Windows account, or tool configuration. `orquestrador` may transport the file,
but a
deployment operator must enforce the profile tool allowlist, Windows account, ACLs,
and environment isolation. At runtime, `orquestrador` calls the Sibyla API for
channel intake; the Sibyla Worker invokes `documental-agent` directly for extraction
and invokes `financial-agent` directly for bounded proposal jobs.

## F0 verification boundary

Repository tests validate the domain model, state machine, job lifecycle, audit
guard, storage clients, and configuration rules. Before declaring F0 complete on
the target host, record the external spike evidence listed in
`docs/project-todo.md`.
