# Apollo licence channels — implementation status, 2026-09-08

## Delivered: configuration and paused staging, not live connectors

The configuration model supports WhatsApp (Hermes), Telegram (bot), and email
(Microsoft Graph, Gmail, IMAP). This is the first implementation slice. It does
not yet make those new channel configurations operational or implement document
link downloads. No production gateway was changed or started.

Tables introduced by the two pending migrations:

| Table | Responsibility |
| --- | --- |
| `aichan` | Licence/profile, provider/account, credential references, document-intake authorization, desired enabled state and revision |
| `aichscope` | Exact allowed chat/folder resources and document-download host allowlist |
| `aicron` | Channel-owned email triage/intake tasks and interval in minutes |
| `credst` (existing) | Protected provider and intake API credentials; never returned by configuration reads |
| `chnbnd` (existing) | Intake API client identity binding to the licence |

Composite foreign keys prevent cross-licence profile, credential and child-row
references. Forced RLS requires the selected owner and administration privilege.
Application checks also require licence administration; being superuser does not
allow configuration reads for a different selected owner. Saves use optimistic
revision checks and audit metadata without credential values.

Enabling desired configuration requires explicit standing permission for incoming
document intake, exact resources, suitable credentials and an intake client bound
to the licence. The protected intake credential must contain that same client ID.
Manifest generation rechecks profile, credential and binding availability.

This standing permission is **not** consent for general email/chat history searches
or sending messages. Those searches continue to require explicit user requests.
Legacy Graph triage can move/category messages; it is not a read-only mailbox
workflow. Future connector scopes must separately account for these operations.

## Administration API

All routes require the existing `OrganizationAdministration` policy and selected
licence. The administration page is `/admin/apollo-channels`, linked alongside
Storage and ERP Connections. It uses the existing Admin panels, connection list
and inline configuration form, not the Documents layout. PT and EN are supported.
Administrators select an assigned Hermes profile, configure exact resources and
schedules, and create or replace protected credentials. Empty credential fields
retain the current reference. The interface explicitly distinguishes desired
activation from operational availability and never starts a gateway.

- `GET /api/ai/channels`: configuration metadata, no secrets.
- `GET /api/ai/channels/manifest`: versioned, secret-free desired runtime state.
- `GET /api/ai/channels/antiforgery`: obtain the request token/header name using
  the same authenticated cookie session as subsequent writes.
- `POST /api/ai/channels/credentials`: protected credential creation; returns ID.
- `POST /api/ai/channels`: create/update configuration; updates require ID and
  expected revision. Disabling remains possible after prerequisites are revoked.

POST requests require the antiforgery token in the returned header and the same
cookies. API DTO definitions are in `ApolloChannelConfiguration.cs`. Credential
field sets are provider-specific and reject unknown fields. Do not paste real
credentials into logs, documentation, shell history or version control.

The API does not register IdP clients, pair WhatsApp, inject runtime credentials,
or export secrets. Each licence needs its own valid, bound intake identity.
The Python intake client now supports an explicit identity pin through
`APOLLO_CHANNEL_INTAKE_EXPECTED_CLIENT_ID`; `APOLLO_CHANNEL_INTAKE_CLIENT_ID` must
match it exactly. Without that pin, the legacy `sibyla-channel-intake` identity
restriction remains. Provisioning must supply the pin from the approved manifest,
not from incoming messages. This guard is not authorization: the API still resolves
the authenticated identity through its licence binding. Token caching includes
the account, authority, API destination, scope and credential fingerprint, and
each API operation uses a single configuration snapshot. No runtime environment
was modified or provisioned by this change.

## Native Hermes cron staging

`local/hermes/licence-gateway/reconcile.py` accepts `--manifest`, `--channel` and
`--home`. Without `--stage` it only validates and reports a dry run. Staging
requires an existing dedicated directory named exactly the manifest runtime key
(`apollo-<owner UUID without hyphens>-<channel UUID without hyphens>`).

Using the installed Hermes Python environment, `--stage` writes channel metadata
and reconciles native cron jobs through Hermes APIs. Jobs are always **paused**,
use fixed worker names, have `no_agent=true`, and cannot run from this operation.
The utility does not install those workers. Repeated staging preserves job
identity; obsolete managed jobs are paused; unrelated jobs are untouched.
An OS lock serializes staging and older revisions are rejected.

Do not point staging at `C:\Users\Administrator\AppData\Local\hermes`.
That existing default home remains the operational baseline, with
`apollo-email-gateway` and `apollo-email-intake` running every two minutes.
The unrelated daily backup task must remain untouched. Existing email intake
handles attachments; document-link intake is not implemented there.

## Required next slice before activation

1. Validate the administration UI against the real migrated database during the
   coordinated release; local interaction tests use synthetic configuration data.
2. Supply licence-specific provider adapters/workers and credential injection,
   Graph folder-role mapping, WhatsApp pairing and provisioned intake identities.
   Extend the intake contract for Telegram; implement Gmail and IMAP intake.
3. Implement document-link retrieval with destination/DNS/redirect validation,
   private-network blocking, download limits and file validation. The configured
   hostname allowlist alone is not an SSRF defence.
4. Connect desired-state publication and runtime health/revocation handling.
   A manually exported manifest is not an automatic DB-to-gateway synchronizer.
5. Run sealed end-to-end acceptance: attachments and links, deduplication/retry,
   licence isolation, disallowed resources, revocation and explicit-only history.
6. Apply migrations to Main and Preview in the approved parity-preserving release
   workflow, then explicitly cut over one validated licence/channel at a time.

Do not unpause staged jobs until their scoped workers and prerequisites exist.

## Validation and deployment boundaries

Targeted tests cover validation, encrypted credential storage, auditing,
cross-licence denial, composite foreign keys, forced RLS without tenant context,
revision conflicts, HTTP administration authorization and antiforgery.
Both migrations were exercised in a uniquely named temporary Preview schema and
that schema was removed afterward. Public Preview/Main schemas and migration
histories were not changed.

Python unit tests and an installed-Hermes runtime contract check cover idempotent
paused staging and preservation of unrelated jobs, without gateway execution.
No live provider acceptance result is claimed. No commit, push or deployment is
included in this implementation slice.

The full suite also checks that every compiled migration is already applied to
the shared Preview database (`HealthSchemaAnswersTheRoleAndTheAppliedMigrationsWithoutAToken`).
That check fails while these two migrations remain pending. Do not waive that
assertion or apply migrations outside the approved release workflow merely to
make this local run green; rerun it after the coordinated schema update.

Follow-up client validation: 64 Python tests passed using the isolated system
Python environment. Running the same suite with the installed Hermes interpreter
exposed an existing environment-dependent envelope test: it assumes the gateway
identity module is unavailable, whereas the installed interpreter can import it.
That assertion was not weakened. The new client tests passed in both environments.

Administration UI validation: 10 targeted Platform tests passed, including
owner-filtered profile selection. Six browser/API tests passed, including the
Playwright form workflow in PT/EN at 1440px and 390px with synthetic data. Screenshots
are under `local/artifacts/playwright/apollo-channels`. Form controls remain disabled
until the interactive circuit is ready, preventing prerender hydration from
discarding early input. Database migration and live provider acceptance remain
separate release gates.
