Files
Assistant/docs/FIRST_EXTERNAL_OPERATION_PROPOSAL.md
m 45fb5f8709
CI / test (pull_request) Successful in 10s
CI / test (push) Successful in 10s
architecture: accept prospective notification delivery
2026-08-16 18:58:09 +03:00

9.4 KiB

First external operation proposal

Status: ACCEPTED by ADR-0005

Task: ARCH-000005, revision 1

Base: 91d03b730d1bdbbe8e67b831fb21046768c4df5e

Objective

Choose the smallest useful real-world operation that exercises Domovoy's existing safety path without creating a generic mutation escape hatch:

Creator session → owner validation → live Work justification → Capability binding revision → Authority validation → durable Commit → isolated Executor → external receipt → durable attempt/outcome → read-only reconciliation

The proposal task changed no runtime, contract, schema, credential, endpoint, or deployment state. Its recommendation and the local Windows display channel were subsequently accepted by ADR-0005 under task ARCH-000006.

Current gap

Foundation and the Creator console prove internal Continuity execution, durable attempts, UNKNOWN_OUTCOME, and reconciliation. No production Executor currently mutates a system outside Assistant-owned canonical state. Before adding one, V1 needs an explicit decision for:

  • the semantic operation and subject;
  • who may request it and with which live justification;
  • how a destination is bound, revised, disabled, and recovered;
  • which exact external protocol makes duplicate prevention and reconciliation provable;
  • where credentials live and how they rotate;
  • which ambiguous failures become UNKNOWN_OUTCOME.

Alternatives considered

Deliver one server-constructed notification about one Creator-owned ProspectiveItem to one preconfigured Creator destination. This extends existing Continuity product behavior while keeping delivery distinct from satisfaction or commitment fulfilment.

The semantic operation is deliver-prospective-notification-v1; HTTP is only its first adapter mechanism.

Arbitrary webhook — rejected

Accepting a URL, headers, method, or free-form JSON from a browser/model would be a universal mutation capability, an SSRF surface, and a substitute for typed domain contracts. It cannot be the first operation.

Direct email, Telegram, or chat-provider send — deferred

These are useful delivery mechanisms but their duplicate and reconciliation semantics differ. Selecting one before a recipient and provider contract exist would embed provider behavior into the domain decision. A provider may be used later only behind the accepted typed operation and Executor port.

File or NAS export — deferred

An export is mechanically idempotent, but meaningful payload export requires Artifact metadata/payload lifecycle that V1 has not implemented. Creating unowned files first would bypass Artifact ownership questions.

Git mutation — rejected for V1

Repository mutation belongs to the separately deferred Development Control Plane and would give the first product Executor disproportionate authority.

Proposed semantic operation

Capability: prospective-notification-delivery

Operation: deliver-prospective-notification-v1

Subject and immutable commit evidence:

  • ProspectiveItemID and expected Prospective revision;
  • owner Principal, Scope and Goal observed from Continuity;
  • live Work TaskTarget justification and revision;
  • server-selected notification template code prospective-delivered-v1;
  • current CapabilityBindingID and binding revision;
  • ActionIntentID, AuthorizedAction ID and authorization time.

The browser supplies only the selected Prospective item, expected revision, Work target and an explicit confirmation. It never supplies Actor, Principal, grant, capability, operation, destination URL, authorization header, template, message body, Action IDs, or provider fields.

The owner must validate that the Prospective item belongs to the authenticated Principal and is currently DELIVERED. Notification success does not change the Prospective item to SATISFIED, fulfil a Commitment, or prove that a human read the message.

Destination binding and secret lifecycle

The existing Capability owner already owns CapabilityBinding. An implementation should materialize a versioned binding rather than use ambient configuration as correctness state. Minimum binding meaning:

  • stable Binding ID and revision;
  • owner Principal and capability ID;
  • fixed HTTPS origin and fixed delivery/receipt path template;
  • active/retired lifecycle;
  • declared idempotency and reconciliation protocol version;
  • external secret reference, never literal secret material;
  • verification time and health state.

Commit records the binding ID and revision used for authorization. A retired, missing, unhealthy, owner-mismatched, or revised binding fails closed before a new external attempt. Historical actions retain their original binding evidence. Rotation creates a new secret version/reference; it does not rewrite old envelopes.

Secret bytes are mounted only into the isolated Commit Runtime Executor. They must not enter PostgreSQL, Creator responses, browser storage, image layers, task packets, Git, logs, or cognitive context.

Required external protocol

The first adapter may use HTTPS only if the selected destination implements a narrow receipt protocol:

  1. PUT <fixed-delivery-path>/<ActionIntentID> creates the delivery exactly once.
  2. The body is the server-rendered typed V1 notification; its canonical digest is recorded before execution.
  3. Repeating the same intent and digest returns the original receipt without redelivery.
  4. Reusing an intent with a different digest returns an integrity conflict without mutation.
  5. GET <fixed-receipt-path>/<ActionIntentID> is read-only and returns whether the delivery exists plus the stored digest/receipt identity.
  6. Success is accepted only when the receipt identity and digest match the committed request.

The endpoint origin and paths come only from the validated binding. Redirects are disabled. DNS resolution, certificate validation, response size, content type and timeouts are bounded. No request field can select another host, scheme, path, header or credential.

Failure and reconciliation semantics

  • Binding/authority/owner/revision failure: reject before Commit or external attempt as appropriate.
  • Definite protocol rejection before mutation: FAILED with typed reason.
  • Timeout, connection loss after send, malformed success, unexpected 5xx, or receipt mismatch: UNKNOWN_OUTCOME.
  • UNKNOWN_OUTCOME blocks another delivery attempt for the same action.
  • Explicit reconciliation issues only the receipt GET and creates a new attempt identity.
  • Matching receipt/digest reconciles to SUCCEEDED; explicit absence remains unknown unless the provider contract proves absence is authoritative after its consistency window.
  • Executor restart loads the immutable envelope and latest attempt; no process-local queue, mutex or timer is correctness state.
  • Binding retirement blocks new authorization but preserves reconciliation access for historical actions until an explicit recovery/retention decision exists.

Security constraints

  • No arbitrary URL, HTTP method, headers, body, template or provider operation.
  • No redirects and no fallback host.
  • No ambient machine credential or credential shared with another capability.
  • No direct call from UI, Core, Continuity, cognitive worker or model adapter.
  • No logging of body, bearer secret or full provider response.
  • No automatic retry of ambiguous mutation.
  • No implication that delivery equals reading, satisfaction or fulfilment.
  • No docker-test deployment or verification.

Acceptance evidence required from an implementation

  • adversarial browser/model fields cannot alter the operation or destination;
  • foreign owner, wrong Prospective state, stale item/Work/binding revision, revoked grant and unhealthy binding are denied;
  • duplicate delivery with the same intent produces one external receipt;
  • lost acknowledgement records UNKNOWN_OUTCOME and read-only reconciliation finds the original receipt;
  • mismatched digest is an integrity failure and never redelivers;
  • restart recovery uses durable Commit state and provider receipt only;
  • secret rotation and binding retirement fail closed without rewriting history;
  • Commit Runtime is the only component with the delivery credential;
  • production smoke uses the selected real destination and does not use docker-test.

Implementation slices after acceptance

  1. Accept the semantic/protocol decision in an ADR-level task; confirm that the existing registry dependencies remain sufficient.
  2. Implement Capability-owned binding lifecycle and owner-local persistence, with only an external secret reference.
  3. Extend the typed Commit envelope for the notification subject, binding revision and request digest.
  4. Implement a deterministic fake receiver and adversarial/idempotency/recovery tests.
  5. Implement the isolated HTTPS Executor/reconciler with strict destination policy and secret-file injection.
  6. Add the explicit Creator confirmation flow and action-history rendering.
  7. Configure one concrete compatible destination, generate its credential outside Git, deploy on docker-prod, and run exact-artifact production smoke.

Resolution

ADR-0005 accepts this typed operation and protocol. The first compatible receiver will run on docker-prod behind the existing https://assistent.cin.su origin, and a separately authorized local Windows agent will show accepted receipts to the Creator. Receiver acceptance is the external Commit outcome; operating-system display and human reading are not.