@wildo-ai/saas-backend-lib 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/base-classes/models/manual-controllers.backend.definition.d.ts.map +1 -1
- package/dist/esm/base-classes/models/manual-controllers.backend.definition.js +2 -0
- package/dist/esm/base-classes/models/manual-controllers.backend.definition.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts +4 -0
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js +20 -5
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts +3 -0
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js +10 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts +22 -0
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js +25 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts +38 -3
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js +68 -5
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts +2 -2
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js +6 -3
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts +83 -0
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +217 -14
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts +32 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js +36 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts +38 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js +83 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts +36 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js +51 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts +46 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js +68 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts +27 -0
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js +42 -3
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts +26 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js +10 -0
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts +10 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js +60 -28
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts +90 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js +373 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js.map +1 -0
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts +29 -2
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.js +40 -3
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js +34 -6
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts +30 -6
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js +67 -22
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js +18 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts +66 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js +161 -14
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts +47 -0
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.js.map +1 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.d.ts +27 -2
- package/dist/esm/external-providers/services/content-safety.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.js +46 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/embeddings.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/embeddings.backend.service.js +2 -0
- package/dist/esm/external-providers/services/embeddings.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +11 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +23 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts +19 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts.map +1 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.js +41 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.js.map +1 -1
- package/dist/esm/files/utils/file-parent-read-context.backend.d.ts +27 -2
- package/dist/esm/files/utils/file-parent-read-context.backend.d.ts.map +1 -1
- package/dist/esm/files/utils/file-parent-read-context.backend.js +23 -4
- package/dist/esm/files/utils/file-parent-read-context.backend.js.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts +26 -0
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js +100 -8
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts +3 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js +8 -2
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js.map +1 -1
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts +43 -0
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts.map +1 -1
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.js +37 -0
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.js.map +1 -1
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts +7 -0
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js +8 -3
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +31 -6
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +78 -30
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js +4 -0
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts +28 -2
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.js +132 -22
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js +12 -8
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts +5 -0
- package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/flows-actors-registry.service.js +18 -2
- package/dist/esm/flows-actors/services/flows-actors-registry.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +33 -0
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +58 -0
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts +12 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js +23 -4
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts +12 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js +55 -17
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts +22 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js +52 -9
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts +79 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts.map +1 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js +211 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js.map +1 -0
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts +2 -2
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js +9 -1
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/inject/models/container.d.ts.map +1 -1
- package/dist/esm/inject/models/container.js +6 -0
- package/dist/esm/inject/models/container.js.map +1 -1
- package/dist/esm/inject/models/service-types.d.ts +5 -0
- package/dist/esm/inject/models/service-types.d.ts.map +1 -1
- package/dist/esm/inject/models/service-types.js +3 -0
- package/dist/esm/inject/models/service-types.js.map +1 -1
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts +5 -0
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts.map +1 -1
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.js.map +1 -1
- package/dist/esm/monitoring/observability/services/metrics.service.d.ts +34 -1
- package/dist/esm/monitoring/observability/services/metrics.service.d.ts.map +1 -1
- package/dist/esm/monitoring/observability/services/metrics.service.js +49 -0
- package/dist/esm/monitoring/observability/services/metrics.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts +6 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js +15 -4
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js +3 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/http-server.backend.service.d.ts +3 -1
- package/dist/esm/runtime/services/http-server.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/http-server.backend.service.js +11 -3
- package/dist/esm/runtime/services/http-server.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts +22 -0
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts.map +1 -1
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js +54 -0
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts +51 -0
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js +87 -15
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js +5 -2
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js +65 -24
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts +64 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts.map +1 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js +84 -2
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts +14 -0
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js +13 -5
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js +6 -2
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts +7 -0
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js +65 -20
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js +11 -4
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-self-custom-implementations.backend.d.ts.map +1 -1
- package/dist/esm/users/services/user-self-custom-implementations.backend.js +4 -2
- package/dist/esm/users/services/user-self-custom-implementations.backend.js.map +1 -1
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js +16 -0
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +10 -10
|
@@ -49,6 +49,16 @@ export const ApprovalSealsAdminEndpointPath = {
|
|
|
49
49
|
* which no copy can be made (`reason` names why, `detail` says more).
|
|
50
50
|
*/
|
|
51
51
|
MANIFESTED_COPY_REBUILD: '/manifested-copy/rebuild',
|
|
52
|
+
/**
|
|
53
|
+
* POST — whether ONE seal's approval is still CURRENT over the record it is about (#2914): `{ "sealId": "…" }` →
|
|
54
|
+
* {@link ApprovalSealCurrencyResult}. The seal's presentation carries the values of the fields its tool declared the
|
|
55
|
+
* approval covers (`sealedRecordFields`); they are compared with the record as it is now. CURRENT right after the
|
|
56
|
+
* approval, NO_LONGER_CURRENT once one of them changed, SUBJECT_GONE when the record is gone, NO_SEALED_FIELD_SET when
|
|
57
|
+
* the seal covers no record field. Names which fields moved, never a value, so it is not recorded on the trail. 404 for
|
|
58
|
+
* an unknown seal (a seal its approver's erasure retained is not read here); 409 for one whose presentation file is
|
|
59
|
+
* unreadable or not the one its row recorded.
|
|
60
|
+
*/
|
|
61
|
+
CURRENCY: '/currency',
|
|
52
62
|
};
|
|
53
63
|
/**
|
|
54
64
|
* What became of one seal signature the signing log records, against the application's stored seals (#2932). A
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-seals-admin.backend.contracts.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.contracts.ts"],"names":[],"mappings":"AAOA,oHAAoH;AACpH;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,0BAAmC,CAAC;AAEpF,kCAAkC;AAClC,MAAM,CAAC,MAAM,mCAAmC,GAAG,gBAAyB,CAAC;AAE7E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,uCAAuC,GAAG,gCAAyC,CAAC;AAEjG,8EAA8E;AAC9E,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,uGAAuG;IACvG,IAAI,EAAE,OAAO;IACb;;;;;OAKG;IACH,KAAK,EAAE,QAAQ;IACf;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS;IACjB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,qBAAqB;IACzC;;;;;;;;OAQG;IACH,uBAAuB,EAAE,0BAA0B;CAC3C,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAN,IAAY,sBAqBX;AArBD,WAAY,sBAAsB;IAChC,4EAA4E;IAC5E,2CAAiB,CAAA;IACjB;;;;;OAKG;IACH,+CAAqB,CAAA;IACrB;;;OAGG;IACH,qDAA2B,CAAA;IAC3B;;;;OAIG;IACH,iDAAuB,CAAA;AACzB,CAAC,EArBW,sBAAsB,KAAtB,sBAAsB,QAqBjC;AAiED,iEAAiE","sourcesContent":["/**\n * Public contracts of the approval-seals operator door (#2903): literals and types only, so the CLI, tests and scripts\n * use the paths without the DI container (the split the data-seeding and external-data admin surfaces use).\n */\nimport type { ApprovalSealStatus, ApprovalSource, CryptoKey_SignatureRefusal } from '@wildo-ai/saas-models';\nimport type { SigningLogExportFile } from '@wildo-ai/platform-config-lib';\n\n/** @wildo_source:part:start saas.approval-seals.admin-endpoints facet:kind:framework-api facet:audience:operator */\n/**\n * The secret these endpoints require: the application's one admin secret, as the data-seeding and external-data\n * operator doors use. Unset or empty, every endpoint answers 503.\n */\nexport const ApprovalSealsAdminEndpointEnvKey = 'APPLICATION_ADMIN_SECRET' as const;\n\n/** Header carrying the secret. */\nexport const ApprovalSealsAdminEndpointHeaderKey = 'X-Admin-Secret' as const;\n\n/** Root mount: an operator surface, not a consumer API. */\nexport const ApprovalSealsAdminEndpointMountBasePath = '/internal/admin/approval-seals' as const;\n\n/** Sub-paths, relative to {@link ApprovalSealsAdminEndpointMountBasePath}. */\nexport const ApprovalSealsAdminEndpointPath = {\n /** GET — the DEAD seals, oldest first (`?limit=50`, at most 200): each with the refusal it died on. */\n DEAD: '/dead',\n /**\n * POST — re-drive ONE DEAD seal: `{ \"sealId\": \"…\", \"justification\": \"…\", \"operator\"?: \"…\" }`. The seal goes back to\n * PENDING, due now, its claims and decision instant unchanged, so the seal the custodian then signs says when the\n * person decided and the verifier shows it late. The justification is required and recorded\n * (`approval_seal_retried`). 404 for an unknown seal, 409 for one that is not DEAD.\n */\n RETRY: '/retry',\n /**\n * POST — the verification bundle of ONE SEALED seal (`{ \"sealId\": \"…\", \"operator\"?: \"…\", \"justification\"?: \"…\" }`),\n * exactly what `wildo seal verify` reads, recorded on the trail (`approval_seal_exported`): the JWS, the\n * stored record of its key, its issuer, and every disclosure the row still holds (the people not erased, the\n * presentation, the statement). 404 for an unknown seal; 409 for one not SEALED, whose key record is not held, or whose\n * frozen presentation file is not the one its row recorded. A seal retained after its approver's erasure is revealed\n * only with `operator` and `justification`, recorded as a reveal of retained data; its bundle verifies with that\n * person's content reported erased.\n */\n BUNDLE: '/bundle',\n /**\n * POST — an export of the application's signing log (#2932), for the operator to keep where the host cannot rewrite\n * it: `{ \"afterSequence\": 0, \"limit\"?: 1000 }` → the export (a JWS the custodian signed under the application's\n * SIGNING_LOG_EXPORT key), the stored record of that key, `throughSequence` (the `afterSequence` of the next pull), and\n * how its seal signatures reconcile with the stored seals: an UNACCOUNTED one is a signature nobody stores.\n * The export names no person: per entry, the seal's id, its digests, its key and its instant. A custodian refusal\n * answers 503 when asking again can succeed and 409 otherwise, with the refusal as `code`; 409 as well when the\n * application does not hold the export key's record yet.\n */\n SIGNING_LOG_EXPORT: '/signing-log/export',\n /**\n * POST — rebuild the manifested copy of ONE SEALED seal's approved PDF (#2907): `{ \"sealId\": \"…\", \"justification\": \"…\",\n * \"operator\"?: \"…\" }`. Only when the seal names no copy, or names one whose file is gone, unlinked or no longer\n * servable; the copy is made exactly as the relay makes it, from the attested original and the seal, and the seal names\n * it. The justification is required and recorded (`approval_seal_manifested_copy_rebuilt`) before the seal names the\n * copy; when it cannot be recorded the copy is discarded and the rebuild refused. 404 for an unknown seal (a seal whose approver was erased is not found here: the copy prints their\n * name, and nothing may print it again); 409 for one not SEALED, with no file deliverable, whose copy is present, or of\n * which no copy can be made (`reason` names why, `detail` says more).\n */\n MANIFESTED_COPY_REBUILD: '/manifested-copy/rebuild',\n} as const;\n\n/**\n * What became of one seal signature the signing log records, against the application's stored seals (#2932). A\n * logged seal counts as accounted for only when the application stores THAT signature (STORED); the other two are\n * reported, and fail the operator's pull.\n */\nexport enum SigningLogSealStanding {\n /** The application stores this seal, signed with exactly this signature. */\n STORED = 'stored',\n /**\n * The application stores the decision, under another signature of it or none yet. Not accounted for: a re-signature\n * of a real decision is how a backdated seal would look. Benign causes exist (an answer lost on the way back, so the\n * relay asked again; a signature the relay is still writing, which a pull of the same range later shows STORED), and\n * the signature's instant tells them apart.\n */\n RESIGNED = 'resigned',\n /**\n * No stored seal has this decision, and no record says it was destroyed: a signature the custodian made that the\n * application does not hold.\n */\n UNACCOUNTED = 'unaccounted',\n /**\n * The seal is not stored because it was destroyed with the record it attested (#2924): an `APPROVAL_EVIDENCE_DESTROYED`\n * row names this signature's `jti` and decision. Accounted for: evidence follows its record, and the record's removal\n * is what destroyed it. A destroyed seal that had been signed must have been signed with exactly these claims.\n */\n DESTROYED = 'destroyed',\n}\n\n/** One seal signature of an export that the application does not store. Names no person. */\nexport interface SigningLogUnaccountedSeal {\n readonly sequence: number;\n readonly jti: string;\n readonly kid: string;\n /** When the custodian signed it, epoch seconds. */\n readonly iat: number;\n}\n\n/** How an export's seal signatures reconcile with the stored seals. Only APPROVAL_SEAL entries are seals. */\nexport interface SigningLogReconciliation {\n readonly seals: number;\n readonly stored: number;\n /** Signatures of a stored decision that the application does not store (RESIGNED). */\n readonly resigned: readonly SigningLogUnaccountedSeal[];\n /** Signatures of a decision no stored seal has (UNACCOUNTED). */\n readonly unaccounted: readonly SigningLogUnaccountedSeal[];\n /** Signatures of seals destroyed with their record (DESTROYED, #2924): accounted for, listed so the operator sees them. */\n readonly destroyed: readonly SigningLogUnaccountedSeal[];\n}\n\n/** `POST /signing-log/export`'s answer: what the operator keeps (`jws`, `key`), where its next pull starts, and the reconciliation. */\nexport interface ApprovalSealSigningLogExportAnswer extends SigningLogExportFile {\n /** The chain's head when the export was made. */\n readonly headSequence: number;\n /** The last sequence the export carries: the next pull's `afterSequence`. */\n readonly throughSequence: number;\n readonly reconciliation: SigningLogReconciliation;\n}\n\n/** One DEAD seal, as `GET /dead` lists it. Names no person: the approver is in the seal's disclosure only. */\nexport interface ApprovalSealDeadListingEntry {\n readonly sealId: string;\n readonly status: ApprovalSealStatus;\n readonly source: ApprovalSource;\n readonly decisionRef: string;\n readonly step: number;\n readonly sequence: number;\n readonly organizationId: string | null;\n readonly linkedResourceType: string;\n readonly linkedResourceId: string;\n readonly decidedAt: string;\n readonly deadAt: string | null;\n readonly attemptCount: number;\n readonly lastRefusal: CryptoKey_SignatureRefusal | null;\n}\n\n/** `POST /retry`'s answer. */\nexport interface ApprovalSealRetryResult {\n readonly sealId: string;\n readonly status: ApprovalSealStatus;\n /** True when the relay's immediate attempt signed it; false when the batch will. */\n readonly sealed: boolean;\n}\n\n/** `POST /manifested-copy/rebuild`'s answer. */\nexport interface ApprovalSealManifestedCopyRebuildResult {\n readonly sealId: string;\n /** The new copy's file. */\n readonly fileId: string;\n /** The copy the seal named before, when it named one. */\n readonly replacedFileId?: string;\n}\n/** @wildo_source:part:end saas.approval-seals.admin-endpoints */\n"]}
|
|
1
|
+
{"version":3,"file":"approval-seals-admin.backend.contracts.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.contracts.ts"],"names":[],"mappings":"AAOA,oHAAoH;AACpH;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,0BAAmC,CAAC;AAEpF,kCAAkC;AAClC,MAAM,CAAC,MAAM,mCAAmC,GAAG,gBAAyB,CAAC;AAE7E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,uCAAuC,GAAG,gCAAyC,CAAC;AAEjG,8EAA8E;AAC9E,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,uGAAuG;IACvG,IAAI,EAAE,OAAO;IACb;;;;;OAKG;IACH,KAAK,EAAE,QAAQ;IACf;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS;IACjB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,qBAAqB;IACzC;;;;;;;;OAQG;IACH,uBAAuB,EAAE,0BAA0B;IACnD;;;;;;;;OAQG;IACH,QAAQ,EAAE,WAAW;CACb,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAN,IAAY,sBAqBX;AArBD,WAAY,sBAAsB;IAChC,4EAA4E;IAC5E,2CAAiB,CAAA;IACjB;;;;;OAKG;IACH,+CAAqB,CAAA;IACrB;;;OAGG;IACH,qDAA2B,CAAA;IAC3B;;;;OAIG;IACH,iDAAuB,CAAA;AACzB,CAAC,EArBW,sBAAsB,KAAtB,sBAAsB,QAqBjC;AA2ED,iEAAiE","sourcesContent":["/**\n * Public contracts of the approval-seals operator door (#2903): literals and types only, so the CLI, tests and scripts\n * use the paths without the DI container (the split the data-seeding and external-data admin surfaces use).\n */\nimport type { ApprovalSealStatus, ApprovalSealSubjectCurrency, ApprovalSource, CryptoKey_SignatureRefusal, HumanDecision_PresentedFieldCurrency } from '@wildo-ai/saas-models';\nimport type { SigningLogExportFile } from '@wildo-ai/platform-config-lib';\n\n/** @wildo_source:part:start saas.approval-seals.admin-endpoints facet:kind:framework-api facet:audience:operator */\n/**\n * The secret these endpoints require: the application's one admin secret, as the data-seeding and external-data\n * operator doors use. Unset or empty, every endpoint answers 503.\n */\nexport const ApprovalSealsAdminEndpointEnvKey = 'APPLICATION_ADMIN_SECRET' as const;\n\n/** Header carrying the secret. */\nexport const ApprovalSealsAdminEndpointHeaderKey = 'X-Admin-Secret' as const;\n\n/** Root mount: an operator surface, not a consumer API. */\nexport const ApprovalSealsAdminEndpointMountBasePath = '/internal/admin/approval-seals' as const;\n\n/** Sub-paths, relative to {@link ApprovalSealsAdminEndpointMountBasePath}. */\nexport const ApprovalSealsAdminEndpointPath = {\n /** GET — the DEAD seals, oldest first (`?limit=50`, at most 200): each with the refusal it died on. */\n DEAD: '/dead',\n /**\n * POST — re-drive ONE DEAD seal: `{ \"sealId\": \"…\", \"justification\": \"…\", \"operator\"?: \"…\" }`. The seal goes back to\n * PENDING, due now, its claims and decision instant unchanged, so the seal the custodian then signs says when the\n * person decided and the verifier shows it late. The justification is required and recorded\n * (`approval_seal_retried`). 404 for an unknown seal, 409 for one that is not DEAD.\n */\n RETRY: '/retry',\n /**\n * POST — the verification bundle of ONE SEALED seal (`{ \"sealId\": \"…\", \"operator\"?: \"…\", \"justification\"?: \"…\" }`),\n * exactly what `wildo seal verify` reads, recorded on the trail (`approval_seal_exported`): the JWS, the\n * stored record of its key, its issuer, and every disclosure the row still holds (the people not erased, the\n * presentation, the statement). 404 for an unknown seal; 409 for one not SEALED, whose key record is not held, or whose\n * frozen presentation file is not the one its row recorded. A seal retained after its approver's erasure is revealed\n * only with `operator` and `justification`, recorded as a reveal of retained data; its bundle verifies with that\n * person's content reported erased.\n */\n BUNDLE: '/bundle',\n /**\n * POST — an export of the application's signing log (#2932), for the operator to keep where the host cannot rewrite\n * it: `{ \"afterSequence\": 0, \"limit\"?: 1000 }` → the export (a JWS the custodian signed under the application's\n * SIGNING_LOG_EXPORT key), the stored record of that key, `throughSequence` (the `afterSequence` of the next pull), and\n * how its seal signatures reconcile with the stored seals: an UNACCOUNTED one is a signature nobody stores.\n * The export names no person: per entry, the seal's id, its digests, its key and its instant. A custodian refusal\n * answers 503 when asking again can succeed and 409 otherwise, with the refusal as `code`; 409 as well when the\n * application does not hold the export key's record yet.\n */\n SIGNING_LOG_EXPORT: '/signing-log/export',\n /**\n * POST — rebuild the manifested copy of ONE SEALED seal's approved PDF (#2907): `{ \"sealId\": \"…\", \"justification\": \"…\",\n * \"operator\"?: \"…\" }`. Only when the seal names no copy, or names one whose file is gone, unlinked or no longer\n * servable; the copy is made exactly as the relay makes it, from the attested original and the seal, and the seal names\n * it. The justification is required and recorded (`approval_seal_manifested_copy_rebuilt`) before the seal names the\n * copy; when it cannot be recorded the copy is discarded and the rebuild refused. 404 for an unknown seal (a seal whose approver was erased is not found here: the copy prints their\n * name, and nothing may print it again); 409 for one not SEALED, with no file deliverable, whose copy is present, or of\n * which no copy can be made (`reason` names why, `detail` says more).\n */\n MANIFESTED_COPY_REBUILD: '/manifested-copy/rebuild',\n /**\n * POST — whether ONE seal's approval is still CURRENT over the record it is about (#2914): `{ \"sealId\": \"…\" }` →\n * {@link ApprovalSealCurrencyResult}. The seal's presentation carries the values of the fields its tool declared the\n * approval covers (`sealedRecordFields`); they are compared with the record as it is now. CURRENT right after the\n * approval, NO_LONGER_CURRENT once one of them changed, SUBJECT_GONE when the record is gone, NO_SEALED_FIELD_SET when\n * the seal covers no record field. Names which fields moved, never a value, so it is not recorded on the trail. 404 for\n * an unknown seal (a seal its approver's erasure retained is not read here); 409 for one whose presentation file is\n * unreadable or not the one its row recorded.\n */\n CURRENCY: '/currency',\n} as const;\n\n/**\n * What became of one seal signature the signing log records, against the application's stored seals (#2932). A\n * logged seal counts as accounted for only when the application stores THAT signature (STORED); the other two are\n * reported, and fail the operator's pull.\n */\nexport enum SigningLogSealStanding {\n /** The application stores this seal, signed with exactly this signature. */\n STORED = 'stored',\n /**\n * The application stores the decision, under another signature of it or none yet. Not accounted for: a re-signature\n * of a real decision is how a backdated seal would look. Benign causes exist (an answer lost on the way back, so the\n * relay asked again; a signature the relay is still writing, which a pull of the same range later shows STORED), and\n * the signature's instant tells them apart.\n */\n RESIGNED = 'resigned',\n /**\n * No stored seal has this decision, and no record says it was destroyed: a signature the custodian made that the\n * application does not hold.\n */\n UNACCOUNTED = 'unaccounted',\n /**\n * The seal is not stored because it was destroyed with the record it attested (#2924): an `APPROVAL_EVIDENCE_DESTROYED`\n * row names this signature's `jti` and decision. Accounted for: evidence follows its record, and the record's removal\n * is what destroyed it. A destroyed seal that had been signed must have been signed with exactly these claims.\n */\n DESTROYED = 'destroyed',\n}\n\n/** One seal signature of an export that the application does not store. Names no person. */\nexport interface SigningLogUnaccountedSeal {\n readonly sequence: number;\n readonly jti: string;\n readonly kid: string;\n /** When the custodian signed it, epoch seconds. */\n readonly iat: number;\n}\n\n/** How an export's seal signatures reconcile with the stored seals. Only APPROVAL_SEAL entries are seals. */\nexport interface SigningLogReconciliation {\n readonly seals: number;\n readonly stored: number;\n /** Signatures of a stored decision that the application does not store (RESIGNED). */\n readonly resigned: readonly SigningLogUnaccountedSeal[];\n /** Signatures of a decision no stored seal has (UNACCOUNTED). */\n readonly unaccounted: readonly SigningLogUnaccountedSeal[];\n /** Signatures of seals destroyed with their record (DESTROYED, #2924): accounted for, listed so the operator sees them. */\n readonly destroyed: readonly SigningLogUnaccountedSeal[];\n}\n\n/** `POST /signing-log/export`'s answer: what the operator keeps (`jws`, `key`), where its next pull starts, and the reconciliation. */\nexport interface ApprovalSealSigningLogExportAnswer extends SigningLogExportFile {\n /** The chain's head when the export was made. */\n readonly headSequence: number;\n /** The last sequence the export carries: the next pull's `afterSequence`. */\n readonly throughSequence: number;\n readonly reconciliation: SigningLogReconciliation;\n}\n\n/** One DEAD seal, as `GET /dead` lists it. Names no person: the approver is in the seal's disclosure only. */\nexport interface ApprovalSealDeadListingEntry {\n readonly sealId: string;\n readonly status: ApprovalSealStatus;\n readonly source: ApprovalSource;\n readonly decisionRef: string;\n readonly step: number;\n readonly sequence: number;\n readonly organizationId: string | null;\n readonly linkedResourceType: string;\n readonly linkedResourceId: string;\n readonly decidedAt: string;\n readonly deadAt: string | null;\n readonly attemptCount: number;\n readonly lastRefusal: CryptoKey_SignatureRefusal | null;\n}\n\n/** `POST /retry`'s answer. */\nexport interface ApprovalSealRetryResult {\n readonly sealId: string;\n readonly status: ApprovalSealStatus;\n /** True when the relay's immediate attempt signed it; false when the batch will. */\n readonly sealed: boolean;\n}\n\n/** `POST /currency`'s answer (#2914). Names no person and no value. */\nexport interface ApprovalSealCurrencyResult {\n readonly sealId: string;\n readonly standing: ApprovalSealSubjectCurrency;\n /** The record compared, as the seal names it; absent for NO_SEALED_FIELD_SET. */\n readonly subject?: { readonly resourceType: string; readonly resourceId: string };\n /** Each sealed field's currency (CURRENT / CHANGED / ABSENT); absent unless compared. */\n readonly fields?: readonly { readonly key: string; readonly currency: HumanDecision_PresentedFieldCurrency }[];\n}\n\n/** `POST /manifested-copy/rebuild`'s answer. */\nexport interface ApprovalSealManifestedCopyRebuildResult {\n readonly sealId: string;\n /** The new copy's file. */\n readonly fileId: string;\n /** The copy the seal named before, when it named one. */\n readonly replacedFileId?: string;\n}\n/** @wildo_source:part:end saas.approval-seals.admin-endpoints */\n"]}
|
|
@@ -58,7 +58,7 @@ export declare class ApprovalSealsAdminBackendController extends AbstractControl
|
|
|
58
58
|
/** #2903 S7: where a presentation moved to a frozen file is read back from. */
|
|
59
59
|
files?: Pick<FilesBackendService, 'readFileBytes' | 'linkFileToResource' | 'deleteDetachedFile'>, contexts?: Pick<ExecutionContextCreatorBackendService, 'createForInternalSystemOperation'>,
|
|
60
60
|
/** #2903 S7: the audited reveal of a seal retained after its approver's erasure. */
|
|
61
|
-
systemAccess?: Pick<SystemAccessBackendService, 'readRetainedAsSystem'>,
|
|
61
|
+
systemAccess?: Pick<SystemAccessBackendService, 'readRetainedAsSystem' | 'readAsSystem'>,
|
|
62
62
|
/** #2932: the custodian's export of the signing log, relayed to the operator. */
|
|
63
63
|
applicationKeys?: Pick<ApplicationKeysBackendService, 'exportSigningLog'>);
|
|
64
64
|
getCustomMountBasePath(): string;
|
|
@@ -78,6 +78,15 @@ export declare class ApprovalSealsAdminBackendController extends AbstractControl
|
|
|
78
78
|
* assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a seal is exported with the key record and disclosures that verify it offline, never re-derived.
|
|
79
79
|
*/
|
|
80
80
|
private handleBundle;
|
|
81
|
+
/**
|
|
82
|
+
* POST /currency — whether one seal's approval is still current over the record it is about (#2914): the values of the
|
|
83
|
+
* fields its tool declared the approval covers, as its presentation carries them, against the record as it is now
|
|
84
|
+
* (`assessApprovalSealCurrency`). Any seal status: a PENDING seal's presentation is already what the person approved.
|
|
85
|
+
* Names which sealed fields moved and never a value, so nothing is recorded on the trail.
|
|
86
|
+
*
|
|
87
|
+
* assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — an operator can ask whether a sealed approval still describes its record, computed from the seal and the record, never assumed.
|
|
88
|
+
*/
|
|
89
|
+
private handleCurrency;
|
|
81
90
|
/**
|
|
82
91
|
* The copy's SHA-256 as its file row recorded it when it was written (#2907): the host's statement that binds what the
|
|
83
92
|
* verifier takes from the copy itself (its words, its verification address). A copy whose row is gone is named without
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-seals-admin.backend.controller.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,kBAAkB,CAAC;AAM1B,OAAO,
|
|
1
|
+
{"version":3,"file":"approval-seals-admin.backend.controller.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,kBAAkB,CAAC;AAM1B,OAAO,EAUL,KAAK,YAAY,EAGlB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,2BAA2B,EAI3B,KAAK,2BAA2B,EAChC,KAAK,8BAA8B,EACpC,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,gCAAgC,EAAE,MAAM,0FAA0F,CAAC;AAC5I,OAAO,EAAE,0BAA0B,EAAE,MAAM,kEAAkE,CAAC;AAC9G,OAAO,EAAE,mBAAmB,EAAE,MAAM,kEAAkE,CAAC;AACvG,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAE7F,OAAO,EAAE,yCAAyC,EAAE,MAAM,qGAAqG,CAAC;AAChK,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gFAAgF,CAAC;AAC9H,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AAC7F,OAAO,EAAoC,KAAK,6BAA6B,EAAE,MAAM,6DAA6D,CAAC;AACnJ,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,wEAAwE,CAAC;AACtI,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,kEAAkE,CAAC;AAC9H,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oEAAoE,CAAC;AAqCrH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,SAAS,2BAA2B,EAAE,GAAG,2BAA2B,EAAE,CAUnJ;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,gBAAgB,GAAG,kBAAkB,CAAC,GAAG,8BAA8B,CAAC,gBAAgB,CAAC,CAOtJ;AA4BD,qBACa,mCAAoC,SAAQ,gCAAgC;IAQhC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IACpF,oFAAoF;IAEpF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAClC,+EAA+E;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAEnF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B,oFAAoF;IACxB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAC1F,iFAAiF;IAClB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IAnBlG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;gBAGV,MAAM,EAAE,aAAa,EACR,YAAY,EAAE,0BAA0B,EACxC,YAAY,EAAE,mBAAmB,EAClB,oBAAoB,EAAE,yCAAyC,EACxD,KAAK,EAAE,IAAI,CAAC,+BAA+B,EAAE,UAAU,GAAG,4BAA4B,CAAC,EACnF,SAAS,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,sBAAsB,CAAC;IAC3I,oFAAoF;IAEnE,gBAAgB,CAAC,EAAE,IAAI,CAAC,uCAAuC,EAAE,OAAO,CAAC;IAC1F,+EAA+E;IACF,KAAK,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,GAAG,oBAAoB,GAAG,oBAAoB,CAAC,EAE5J,QAAQ,CAAC,EAAE,IAAI,CAAC,qCAAqC,EAAE,kCAAkC,CAAC;IAC3G,oFAAoF;IACP,YAAY,CAAC,EAAE,IAAI,CAAC,0BAA0B,EAAE,sBAAsB,GAAG,cAAc,CAAC;IACrK,iFAAiF;IACD,eAAe,CAAC,EAAE,IAAI,CAAC,6BAA6B,EAAE,kBAAkB,CAAC;IAMpJ,sBAAsB,IAAI,MAAM;IAIvC,SAAS,CAAC,WAAW,IAAI,IAAI;IAgB7B,2GAA2G;IAC3G,OAAO,CAAC,kBAAkB;IAwB1B,sDAAsD;YACxC,cAAc;IAW5B;;;;;;;;;OASG;YACW,YAAY;IAyD1B;;;;;;;OAOG;YACW,cAAc;IAkC5B;;;;OAIG;YACW,oBAAoB;IAOlC;;;;;;;;;;;;OAYG;YACW,sBAAsB;IAwDpC;;;;OAIG;YACW,iBAAiB;IAe/B;;;;;OAKG;YACW,cAAc;IAS5B;;;;;OAKG;YACW,qBAAqB;IAkBnC;;;;;;;;;;OAUG;YACW,2BAA2B;IA2CzC,gHAAgH;YAClG,aAAa;IAK3B;;;;;OAKG;YACW,WAAW;CA2B1B"}
|
|
@@ -27,7 +27,7 @@ import { timingSafeEqual } from 'crypto';
|
|
|
27
27
|
import { inject, injectable, optional } from 'inversify';
|
|
28
28
|
import { z } from 'zod';
|
|
29
29
|
import { createHash } from 'crypto';
|
|
30
|
-
import { ApprovalSealSchema, ApprovalSealStatus, CORE_APP_ROLES, CoreResourceType, CryptoKey_Purpose, ErrorCustomMessageReference, ErrorType, FILE_DOWNLOADABLE_STATUSES, } from '@wildo-ai/saas-models';
|
|
30
|
+
import { ApprovalSealSchema, ApprovalSealStatus, CORE_APP_ROLES, CoreResourceType, CryptoKey_Purpose, ErrorCustomMessageReference, ErrorType, FILE_DOWNLOADABLE_STATUSES, isWildoBackendError, } from '@wildo-ai/saas-models';
|
|
31
31
|
import { ApprovalSealDisclosureClaim, ES256_JWS_ALGORITHM, SigningLogExportClaimsSchema, SigningLogExportRequestSchema, } from '@wildo-ai/platform-config-lib';
|
|
32
32
|
import { AbstractControllerBackendService } from '../../base-classes/services/controllers/abstract-controller.backend.service.js';
|
|
33
33
|
import { ErrorBuilderBackendService } from '../../errors/services/error-builder.backend.service.js';
|
|
@@ -40,6 +40,8 @@ import { ApprovalSealRelayStore } from './approval-seal-relay-store.backend.js';
|
|
|
40
40
|
import { reconcileSigningLogSeals } from './signing-log-reconciliation.backend.js';
|
|
41
41
|
import { approvalSealExportedEvent, approvalSealManifestedCopyRebuiltEvent, approvalSealRetriedEvent } from './approval-seal-events.backend.js';
|
|
42
42
|
import { produceApprovalSealManifestedCopy } from './approval-seal-manifested-copy.backend.js';
|
|
43
|
+
import { assessApprovalSealCurrency } from './approval-seal-currency.backend.js';
|
|
44
|
+
import { ApprovalSealPresentationReadOutcome, readApprovalSealPresentation } from './approval-seal-presentation-read.backend.js';
|
|
43
45
|
import { ApprovalSealsAdminEndpointEnvKey, ApprovalSealsAdminEndpointHeaderKey, ApprovalSealsAdminEndpointMountBasePath, ApprovalSealsAdminEndpointPath, } from './approval-seals-admin.backend.contracts.js';
|
|
44
46
|
import { describeCaughtError } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
45
47
|
const ListDeadQuerySchema = z.object({ limit: z.coerce.number().int().min(1).max(200).optional() }).strict();
|
|
@@ -94,6 +96,7 @@ const RebuildCopyRequestSchema = z.object({
|
|
|
94
96
|
justification: z.string().trim().min(1).max(2000),
|
|
95
97
|
operator: z.string().trim().min(1).max(200).optional(),
|
|
96
98
|
}).strict();
|
|
99
|
+
const CurrencyRequestSchema = z.object({ sealId: z.string().trim().min(1) }).strict();
|
|
97
100
|
const RetryRequestSchema = z.object({
|
|
98
101
|
sealId: z.string().trim().min(1),
|
|
99
102
|
/** Why the cause of the refusal is gone: required, recorded on the trail. */
|
|
@@ -143,6 +146,8 @@ let ApprovalSealsAdminBackendController = class ApprovalSealsAdminBackendControl
|
|
|
143
146
|
this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.BUNDLE, 'The verification bundle of one SEALED approval seal, recorded');
|
|
144
147
|
this.router.post(ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, this.asyncHandler(this.handleRebuildManifestedCopy.bind(this)));
|
|
145
148
|
this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, 'Rebuild one SEALED seal\'s manifested copy, justified and recorded');
|
|
149
|
+
this.router.post(ApprovalSealsAdminEndpointPath.CURRENCY, this.asyncHandler(this.handleCurrency.bind(this)));
|
|
150
|
+
this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.CURRENCY, 'Whether one seal\'s approval is still current over the record it is about');
|
|
146
151
|
this.router.post(ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, this.asyncHandler(this.handleSigningLogExport.bind(this)));
|
|
147
152
|
this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, 'An export of the signing log, signed by the custodian, for the operator to keep');
|
|
148
153
|
}
|
|
@@ -252,6 +257,48 @@ let ApprovalSealsAdminBackendController = class ApprovalSealsAdminBackendControl
|
|
|
252
257
|
}));
|
|
253
258
|
res.json(bundle);
|
|
254
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* POST /currency — whether one seal's approval is still current over the record it is about (#2914): the values of the
|
|
262
|
+
* fields its tool declared the approval covers, as its presentation carries them, against the record as it is now
|
|
263
|
+
* (`assessApprovalSealCurrency`). Any seal status: a PENDING seal's presentation is already what the person approved.
|
|
264
|
+
* Names which sealed fields moved and never a value, so nothing is recorded on the trail.
|
|
265
|
+
*
|
|
266
|
+
* assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — an operator can ask whether a sealed approval still describes its record, computed from the seal and the record, never assumed.
|
|
267
|
+
*/
|
|
268
|
+
async handleCurrency(req, res) {
|
|
269
|
+
const { sealId } = CurrencyRequestSchema.parse(req.body);
|
|
270
|
+
const seal = await this.store.read(sealId);
|
|
271
|
+
if (!seal) {
|
|
272
|
+
throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {
|
|
273
|
+
customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId },
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
const presentationContent = await this.presentationContentOf(seal);
|
|
277
|
+
if (presentationContent === PRESENTATION_FILE_TAMPERED || presentationContent === PRESENTATION_FILE_UNREADABLE) {
|
|
278
|
+
throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {
|
|
279
|
+
customMessageReference: ErrorCustomMessageReference.CONFLICT,
|
|
280
|
+
context: { reason: presentationContent === PRESENTATION_FILE_TAMPERED ? 'approval_seal_presentation_file_mismatch' : 'approval_seal_presentation_file_unreadable', sealId },
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const systemAccess = this.systemAccess;
|
|
284
|
+
const assessment = await assessApprovalSealCurrency(seal, presentationContent, {
|
|
285
|
+
readRecord: async (resourceType, resourceId) => {
|
|
286
|
+
if (!systemAccess) {
|
|
287
|
+
throw this.errorBuilder.buildError(ErrorType.CONFIGURATION, undefined, {
|
|
288
|
+
customMessageReference: ErrorCustomMessageReference.CONFIGURATION, context: { message: 'The approval-seal currency check reads the record through the system access service.' },
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return systemAccess.readAsSystem(resourceType, { _id: resourceId }, { suppressSystemAudit: true })
|
|
292
|
+
.catch((error) => {
|
|
293
|
+
if (isWildoBackendError(error) && error.type === ErrorType.NOT_FOUND)
|
|
294
|
+
return null;
|
|
295
|
+
throw error;
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
const result = { sealId, ...assessment };
|
|
300
|
+
res.json(result);
|
|
301
|
+
}
|
|
255
302
|
/**
|
|
256
303
|
* The copy's SHA-256 as its file row recorded it when it was written (#2907): the host's statement that binds what the
|
|
257
304
|
* verifier takes from the copy itself (its words, its verification address). A copy whose row is gone is named without
|
|
@@ -371,34 +418,19 @@ let ApprovalSealsAdminBackendController = class ApprovalSealsAdminBackendControl
|
|
|
371
418
|
* rather than present the presentation as withheld.
|
|
372
419
|
*/
|
|
373
420
|
async presentationContentOf(seal) {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return unreadable('no file service is bound');
|
|
387
|
-
const fileRow = await this.store.readFile(presentation.file.fileId);
|
|
388
|
-
if (!fileRow)
|
|
389
|
-
return unreadable('the file row is gone');
|
|
390
|
-
let bytes;
|
|
391
|
-
try {
|
|
392
|
-
bytes = await this.files.readFileBytes(fileRow, await this.contexts.createForInternalSystemOperation());
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
return unreadable(describeCaughtError(error));
|
|
396
|
-
}
|
|
397
|
-
if (createHash('sha256').update(bytes).digest('hex') !== presentation.file.contentSha256) {
|
|
398
|
-
this.logger.error('[ApprovalSeals] A seal\'s presentation file does not match the hash its row recorded; the export is refused', { sealId: seal._id, fileId: presentation.file.fileId });
|
|
399
|
-
return PRESENTATION_FILE_TAMPERED;
|
|
421
|
+
const read = await readApprovalSealPresentation(seal, { readFileRow: (fileId) => this.store.readFile(fileId), files: this.files, contexts: this.contexts });
|
|
422
|
+
switch (read.outcome) {
|
|
423
|
+
case ApprovalSealPresentationReadOutcome.CONTENT:
|
|
424
|
+
return read.content;
|
|
425
|
+
case ApprovalSealPresentationReadOutcome.NONE:
|
|
426
|
+
return undefined;
|
|
427
|
+
case ApprovalSealPresentationReadOutcome.TAMPERED:
|
|
428
|
+
this.logger.error('[ApprovalSeals] A seal\'s presentation file does not match the hash its row recorded; the export is refused', { sealId: seal._id, fileId: read.fileId });
|
|
429
|
+
return PRESENTATION_FILE_TAMPERED;
|
|
430
|
+
case ApprovalSealPresentationReadOutcome.UNREADABLE:
|
|
431
|
+
this.logger.error('[ApprovalSeals] A seal\'s presentation file cannot be read; the export is refused', { sealId: seal._id, fileId: read.fileId, reason: read.reason });
|
|
432
|
+
return PRESENTATION_FILE_UNREADABLE;
|
|
400
433
|
}
|
|
401
|
-
return JSON.parse(bytes.toString('utf8'));
|
|
402
434
|
}
|
|
403
435
|
/**
|
|
404
436
|
* POST /manifested-copy/rebuild — rebuild the manifested copy of one SEALED seal's approved PDF (#2907): the door past
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-seals-admin.backend.controller.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ApprovalSealsAdminBackendController` — the operator's door onto DEAD approval seals (#2903).\n *\n * A seal goes DEAD only when the custodian refused it for a reason asking again cannot cure: the claims, the digest, the\n * tenant's key scope, the runtime's admission. That refusal is the DEFAULT and stays one; this door is the audited way\n * past it once the cause is fixed (a key re-scoped, a runtime re-admitted). The seal is re-driven, never rewritten: back\n * to PENDING with its claims and decision instant unchanged, so the verifier shows how late it was signed.\n *\n * Posture identical to the data-seeding and external-data operator doors: root-mounted under `/internal/admin/`, gated on\n * `APPLICATION_ADMIN_SECRET` (unset ⇒ 503), the secret compared as two SHA-256 digests with `timingSafeEqual`. The secret\n * names nobody, so the justification (required) and the operator's stated name are recorded as statements.\n */\nimport 'reflect-metadata';\nimport { timingSafeEqual } from 'crypto';\nimport { inject, injectable, optional } from 'inversify';\nimport type { NextFunction, Request, Response } from 'express';\nimport { z } from 'zod';\nimport { createHash } from 'crypto';\nimport {\n ApprovalSealSchema,\n ApprovalSealStatus,\n CORE_APP_ROLES,\n CoreResourceType,\n CryptoKey_Purpose,\n ErrorCustomMessageReference,\n ErrorType,\n FILE_DOWNLOADABLE_STATUSES,\n type ApprovalSeal,\n type FileStatus,\n type File,\n type ResourceType,\n} from '@wildo-ai/saas-models';\nimport {\n ApprovalSealDisclosureClaim,\n ES256_JWS_ALGORITHM,\n SigningLogExportClaimsSchema,\n SigningLogExportRequestSchema,\n type ApprovalSealDisclosureValue,\n type ApprovalSealVerificationBundle,\n} from '@wildo-ai/platform-config-lib';\n\nimport { AbstractControllerBackendService } from '~wildo-backend-lib/base-classes/services/controllers/abstract-controller.backend.service';\nimport { ErrorBuilderBackendService } from '~wildo-backend-lib/errors/services/error-builder.backend.service';\nimport { ErrorHandlerService } from '~wildo-backend-lib/errors/services/error-handler.backend.service';\nimport { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport type { AuditLogsBackendService } from '~wildo-backend-lib/compliance/audit-trails/services/audit-logs.backend.service';\nimport type { ApprovalSealRelayBackendService } from './approval-seal-relay.backend.service';\nimport { ApplicationSignatureRefusedError, type ApplicationKeysBackendService } from '../../security/crypto-keys/application-keys.backend.service';\nimport type { PublishedVerificationKeysBackendService } from '../../security/crypto-keys/published-verification-keys.backend.service';\nimport type { FilesBackendService } from '../../files/services/files.backend.service';\nimport type { ExecutionContextCreatorBackendService } from '../../runtime/services/execution-context-creator.backend.service';\nimport type { SystemAccessBackendService } from '../../base-classes/services/services/system-access.backend.service';\nimport { ApprovalSealRelayStore } from './approval-seal-relay-store.backend';\nimport { reconcileSigningLogSeals } from './signing-log-reconciliation.backend';\nimport { approvalSealExportedEvent, approvalSealManifestedCopyRebuiltEvent, approvalSealRetriedEvent } from './approval-seal-events.backend';\nimport { produceApprovalSealManifestedCopy } from './approval-seal-manifested-copy.backend';\nimport {\n ApprovalSealsAdminEndpointEnvKey,\n ApprovalSealsAdminEndpointHeaderKey,\n ApprovalSealsAdminEndpointMountBasePath,\n ApprovalSealsAdminEndpointPath,\n type ApprovalSealDeadListingEntry,\n type ApprovalSealManifestedCopyRebuildResult,\n type ApprovalSealRetryResult,\n type ApprovalSealSigningLogExportAnswer,\n} from './approval-seals-admin.backend.contracts';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\nconst ListDeadQuerySchema = z.object({ limit: z.coerce.number().int().min(1).max(200).optional() }).strict();\n\nconst BundleRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /**\n * For a seal RETAINED after its approver's erasure: who reveals it and why. A retained row is read only through the\n * audited reveal, which refuses one with no stated person or reason; a seal not retained ignores both.\n */\n operator: z.string().trim().min(1).max(200).optional(),\n justification: z.string().trim().min(1).max(2000).optional(),\n}).strict();\n\n/** What {@link ApprovalSealsAdminBackendController.presentationContentOf} answers for a file that is not what the row recorded. */\nconst PRESENTATION_FILE_TAMPERED = Symbol('presentation-file-tampered');\n/** A presentation file the row names and nothing can read: gone from the files store, or no file service bound (review F1). */\nconst PRESENTATION_FILE_UNREADABLE = Symbol('presentation-file-unreadable');\n\n/**\n * The claims a seal commits to whose content its row no longer holds (#2924). Nothing but an erasure removes a\n * disclosure from a seal's row: the approver's own erasure removes theirs, and a later erasure of the requester or of a\n * person the presentation named removes the requester's disclosure and rewrites the presentation (#3001). So a bundle\n * names them as ERASED, and the verifier reports UNDISCLOSED (erased) / PRESENTATION_ERASED instead of withheld.\n * Computed from the claims the seal SIGNED, so a claim the seal never made is never named.\n */\nexport function erasedClaimsOfSeal(seal: Pick<ApprovalSeal, 'claims'>, given: readonly ApprovalSealDisclosureValue[]): ApprovalSealDisclosureClaim[] {\n const claims = (seal.claims ?? {}) as { approver?: unknown; requester?: { disclosure?: unknown }; presentation?: { digest?: unknown }; statementDigest?: unknown };\n const committed: Array<[ApprovalSealDisclosureClaim, unknown]> = [\n [ApprovalSealDisclosureClaim.APPROVER, claims.approver],\n [ApprovalSealDisclosureClaim.REQUESTER, claims.requester?.disclosure],\n [ApprovalSealDisclosureClaim.PRESENTATION, claims.presentation?.digest],\n [ApprovalSealDisclosureClaim.STATEMENT, claims.statementDigest],\n ];\n const disclosed = new Set(given.map((disclosure) => disclosure.claimName));\n return committed.filter(([claimName, commitment]) => typeof commitment === 'string' && commitment.length > 0 && !disclosed.has(claimName)).map(([claimName]) => claimName);\n}\n\n/**\n * What a bundle says about the deliverable's manifested copy (#2907): its file while the seal still owns it; erased once\n * the approver's erasure removed it (the instant it was made survives, the reference does not); nothing when none was\n * made. Read from the row, so a copy the row no longer names is never reported as merely missing.\n */\nexport function manifestedCopyOfSeal(seal: Pick<ApprovalSeal, 'manifestedCopy' | 'manifestedCopyAt'>): ApprovalSealVerificationBundle['manifestedCopy'] {\n const copy = seal.manifestedCopy as unknown;\n const fileId = copy !== null && typeof copy === 'object' && 'fileId' in copy && typeof (copy as { fileId: unknown }).fileId === 'string'\n ? (copy as { fileId: string }).fileId\n : undefined;\n if (fileId) return { fileId };\n return seal.manifestedCopyAt ? { contentErased: true } : undefined;\n}\n\nconst RebuildCopyRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /** Why the copy is rebuilt (what was wrong, what changed): required, recorded on the trail. */\n justification: z.string().trim().min(1).max(2000),\n operator: z.string().trim().min(1).max(200).optional(),\n}).strict();\n\nconst RetryRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /** Why the cause of the refusal is gone: required, recorded on the trail. */\n justification: z.string().trim().min(1).max(2000),\n /** Who is re-driving it, as the operator states it. The admin secret names nobody, so it is recorded as a statement. */\n operator: z.string().trim().min(1).max(200).optional(),\n}).strict();\n\n/** A compact JWS's payload, parsed; null when it is not JSON. */\nfunction decodedJwsPayloadOf(jws: string): unknown {\n try {\n return JSON.parse(Buffer.from(jws.split('.')[1] ?? '', 'base64url').toString('utf8'));\n } catch {\n return null;\n }\n}\n\n@injectable()\nexport class ApprovalSealsAdminBackendController extends AbstractControllerBackendService {\n private readonly store: ApprovalSealRelayStore;\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.ErrorBuilderService) errorBuilder: ErrorBuilderBackendService,\n @inject(SAAS_SERVICE_TYPES.ErrorHandlerService) errorHandler: ErrorHandlerService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService) repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n @inject(SAAS_SERVICE_TYPES.ApprovalSealRelayService) private readonly relay: Pick<ApprovalSealRelayBackendService, 'sealSoon' | 'manifestedCopyDependencies'>,\n @inject(SAAS_SERVICE_TYPES.AuditLogsService) @optional() private readonly auditLogs?: Pick<AuditLogsBackendService, 'logApprovalSealEvent'>,\n /** #2903 S7: the key records a bundle carries, from the application's own store. */\n @inject(SAAS_SERVICE_TYPES.PublishedVerificationKeysService) @optional()\n private readonly verificationKeys?: Pick<PublishedVerificationKeysBackendService, 'keyOf'>,\n /** #2903 S7: where a presentation moved to a frozen file is read back from. */\n @inject(SAAS_SERVICE_TYPES.FilesBackendService) @optional() private readonly files?: Pick<FilesBackendService, 'readFileBytes' | 'linkFileToResource' | 'deleteDetachedFile'>,\n @inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService) @optional()\n private readonly contexts?: Pick<ExecutionContextCreatorBackendService, 'createForInternalSystemOperation'>,\n /** #2903 S7: the audited reveal of a seal retained after its approver's erasure. */\n @inject(SAAS_SERVICE_TYPES.SystemAccessService) @optional() private readonly systemAccess?: Pick<SystemAccessBackendService, 'readRetainedAsSystem'>,\n /** #2932: the custodian's export of the signing log, relayed to the operator. */\n @inject(SAAS_SERVICE_TYPES.ApplicationKeysService) @optional() private readonly applicationKeys?: Pick<ApplicationKeysBackendService, 'exportSigningLog'>,\n ) {\n super(logger, errorBuilder, errorHandler, 'ApprovalSealsAdminController');\n this.store = new ApprovalSealRelayStore(repositoriesRegistry);\n }\n\n public getCustomMountBasePath(): string {\n return ApprovalSealsAdminEndpointMountBasePath;\n }\n\n protected setupRoutes(): void {\n this.router.use(this.requireAdminSecret.bind(this));\n this.router.get(ApprovalSealsAdminEndpointPath.DEAD, this.asyncHandler(this.handleListDead.bind(this)));\n this.logRouteRegistration('GET', ApprovalSealsAdminEndpointPath.DEAD, 'List the DEAD approval seals');\n this.router.post(ApprovalSealsAdminEndpointPath.RETRY, this.asyncHandler(this.handleRetry.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.RETRY, 'Re-drive one DEAD approval seal, justified');\n this.router.post(ApprovalSealsAdminEndpointPath.BUNDLE, this.asyncHandler(this.handleBundle.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.BUNDLE, 'The verification bundle of one SEALED approval seal, recorded');\n this.router.post(ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, this.asyncHandler(this.handleRebuildManifestedCopy.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, 'Rebuild one SEALED seal\\'s manifested copy, justified and recorded');\n this.router.post(ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, this.asyncHandler(this.handleSigningLogExport.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, 'An export of the signing log, signed by the custodian, for the operator to keep');\n }\n\n /** The guard of the sibling operator doors: both inputs hashed, so `timingSafeEqual` compares 32 bytes. */\n private requireAdminSecret(req: Request, _res: Response, next: NextFunction): void {\n const expectedSecret = process.env[ApprovalSealsAdminEndpointEnvKey];\n if (!expectedSecret) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE,\n context: { message: `approval-seal admin endpoints disabled (${ApprovalSealsAdminEndpointEnvKey} unset)`, envKey: ApprovalSealsAdminEndpointEnvKey },\n });\n }\n const providedSecret = req.headers[ApprovalSealsAdminEndpointHeaderKey.toLowerCase()] as string | undefined;\n if (!providedSecret) {\n throw this.errorBuilder.buildError(ErrorType.AUTHENTICATION, undefined, {\n customMessageReference: ErrorCustomMessageReference.SEED_ADMIN_SECRET_REQUIRED,\n context: { headerKey: ApprovalSealsAdminEndpointHeaderKey },\n });\n }\n if (!timingSafeEqual(createHash('sha256').update(providedSecret).digest(), createHash('sha256').update(expectedSecret).digest())) {\n throw this.errorBuilder.buildError(ErrorType.AUTHENTICATION, undefined, {\n customMessageReference: ErrorCustomMessageReference.SEED_ADMIN_SECRET_INVALID,\n context: { headerKey: ApprovalSealsAdminEndpointHeaderKey },\n });\n }\n next();\n }\n\n /** GET /dead — the DEAD seals, oldest death first. */\n private async handleListDead(req: Request, res: Response): Promise<void> {\n const query = ListDeadQuerySchema.parse(req.query);\n const dead = await this.store.listDead(query.limit ?? 50);\n const seals: ApprovalSealDeadListingEntry[] = dead.map((seal) => ({\n sealId: seal._id, status: seal.status, source: seal.source, decisionRef: seal.decisionRef, step: seal.step, sequence: seal.sequence,\n organizationId: seal.organizationId ?? null, linkedResourceType: seal.linkedResourceType, linkedResourceId: seal.linkedResourceId,\n decidedAt: seal.decidedAt.toISOString(), deadAt: seal.deadAt?.toISOString() ?? null, attemptCount: seal.attemptCount, lastRefusal: seal.lastRefusal ?? null,\n }));\n res.json({ seals });\n }\n\n /**\n * POST /bundle — what an auditor needs to verify one seal offline (#2903 S7): the JWS, its key's stored record, the issuer,\n * and every disclosure the row still holds. A person erased since keeps the seal and gives no disclosure: the bundle\n * names what the row lost (`erasedClaims`, #2924), which the verifier reports as UNDISCLOSED (erased), or for a\n * presentation an erasure rewrote, PRESENTATION_ERASED. A presentation moved to a frozen file is read back and checked against the hash the row\n * recorded before it is handed out as the presentation. A file deliverable the seal attests (#2904) is named with its\n * salt, so a verifier given the file's bytes checks them against the presentation's commitment.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a seal is exported with the key record and disclosures that verify it offline, never re-derived.\n */\n private async handleBundle(req: Request, res: Response): Promise<void> {\n const request = BundleRequestSchema.parse(req.body);\n const { sealId } = request;\n const live = await this.store.read(sealId);\n const seal = live ?? await this.revealRetained(request);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId },\n });\n }\n const conflict = (reason: string) => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason, sealId, status: seal.status },\n });\n if (seal.status !== ApprovalSealStatus.SEALED || !seal.jws || !seal.kid || !seal.iss) throw conflict('approval_seal_not_sealed');\n const key = await this.verificationKeys?.keyOf(seal.kid);\n if (!key) throw conflict('approval_seal_key_record_not_held');\n const disclosures: ApprovalSealDisclosureValue[] = [...(seal.disclosures ?? []), ...(seal.requesterDisclosure ? [seal.requesterDisclosure] : [])].map((disclosure) => ({\n claimName: disclosure.claimName as ApprovalSealDisclosureClaim, salt: disclosure.salt, value: disclosure.value,\n }));\n const presentationContent = await this.presentationContentOf(seal);\n if (presentationContent === PRESENTATION_FILE_TAMPERED) throw conflict('approval_seal_presentation_file_mismatch');\n // Never a bundle without the presentation its seal commits to: the verifier would report it \"not given\", which reads\n // as a choice rather than a loss (#2903 review F1).\n if (presentationContent === PRESENTATION_FILE_UNREADABLE) throw conflict('approval_seal_presentation_file_unreadable');\n if (seal.presentation && presentationContent) {\n disclosures.push({ claimName: ApprovalSealDisclosureClaim.PRESENTATION, salt: seal.presentation.salt, value: presentationContent });\n }\n if (seal.statement) disclosures.push({ claimName: ApprovalSealDisclosureClaim.STATEMENT, salt: seal.statement.salt, value: seal.statement.content });\n const erasedClaims = erasedClaimsOfSeal(seal, disclosures);\n const manifestedCopy = await this.withRecordedCopyHash(manifestedCopyOfSeal(seal));\n const bundle: ApprovalSealVerificationBundle = {\n jws: seal.jws,\n keys: [{\n kid: key.kid, publicJwk: key.publicJwk, alg: ES256_JWS_ALGORITHM, scope: key.scope, scopeId: key.scopeId, iss: key.iss, state: key.state,\n ...(key.compromiseInstant ? { compromiseInstant: key.compromiseInstant.toISOString() } : {}),\n }],\n expectedIssuer: seal.iss,\n disclosures,\n // #2924: what the seal commits to and the row no longer holds, which only an erasure removes, so the verifier\n // reports it UNDISCLOSED (erased) / PRESENTATION_ERASED rather than withheld.\n ...(erasedClaims.length > 0 ? { erasedClaims } : {}),\n // #2904: the file deliverable the seal attests, with the salt that opens its commitment. Not its bytes: the auditor\n // takes those from the file itself (`wildo seal verify --deliverable`), so a file changed since is reported.\n ...(seal.deliverableFileId && seal.deliverableFileSalt ? { deliverableFile: { fileId: seal.deliverableFileId, salt: seal.deliverableFileSalt } } : {}),\n // #2907: the deliverable's manifested copy, or the statement that the approver's erasure deleted it. Not its bytes,\n // which the auditor takes from the file (`wildo seal verify --manifested-copy`).\n ...(manifestedCopy ? { manifestedCopy } : {}),\n };\n // Recorded before anything leaves: the bundle hands the people the seal names to an operator (review L6), and an\n // export that cannot be recorded is refused (review R1).\n await this.recordOperatorAct(seal._id, approvalSealExportedEvent(seal, {\n disclosedClaims: disclosures.map((disclosure) => disclosure.claimName), retained: live === null,\n ...(request.operator ? { operator: request.operator } : {}), ...(request.justification ? { justification: request.justification } : {}),\n }));\n res.json(bundle);\n }\n\n /**\n * The copy's SHA-256 as its file row recorded it when it was written (#2907): the host's statement that binds what the\n * verifier takes from the copy itself (its words, its verification address). A copy whose row is gone is named without\n * it, and the verifier still rebuilds it against the seal.\n */\n private async withRecordedCopyHash(copy: ApprovalSealVerificationBundle['manifestedCopy']): Promise<ApprovalSealVerificationBundle['manifestedCopy']> {\n if (!copy?.fileId) return copy;\n const row = await this.store.readFile(copy.fileId).catch(() => null);\n const contentSha256 = typeof row?.contentSha256 === 'string' ? row.contentSha256 : undefined;\n return contentSha256 ? { ...copy, contentSha256 } : copy;\n }\n\n /**\n * POST /signing-log/export — the custodian's export of the application's signing log (#2932), with the stored record of\n * the key that signed it, for the operator to keep where the host cannot rewrite it. The custodian builds and signs the\n * export from its own log and records its signature there (and on the platform's trail), so the next export lists\n * this one; the export names no person, so this door records nothing more. Its key record comes from the application's\n * own store, which a verifier given the operator's pin set uses only for the public half and the issuer.\n *\n * Each seal signature the export carries is reconciled with the stored seals, here, where the seals are: a signature\n * no stored seal accounts for is reported in the answer and logged as an error. Reconciled at the pull, each entry is\n * reconciled once, when the operator takes it.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — the operator holds signed, hash-chained exports of the signing log outside the host (#2932)\n */\n private async handleSigningLogExport(req: Request, res: Response): Promise<void> {\n const request = SigningLogExportRequestSchema.parse(req.body);\n if (!this.applicationKeys) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'signing_log_export_unavailable', message: 'no application keys service is bound' },\n });\n }\n let answer;\n try {\n answer = await this.applicationKeys.exportSigningLog(request);\n } catch (error) {\n if (!(error instanceof ApplicationSignatureRefusedError)) throw error;\n // KEY_CUSTODIAN_REFUSED admits `code` and `retryable` past the client sanitizer, so the operator reads the refusal.\n throw this.errorBuilder.buildError(error.retryable ? ErrorType.INFRASTRUCTURE : ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.KEY_CUSTODIAN_REFUSED,\n context: { code: error.refusal, retryable: error.retryable, message: error.message },\n });\n }\n const key = await this.verificationKeys?.keyOf(answer.kid);\n if (!key || key.purpose !== CryptoKey_Purpose.SIGNING_LOG_EXPORT) {\n throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason: 'signing_log_export_key_record_not_held', kid: answer.kid },\n });\n }\n const claims = SigningLogExportClaimsSchema.safeParse(decodedJwsPayloadOf(answer.jws));\n if (!claims.success) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'signing_log_export_unreadable', kid: answer.kid },\n });\n }\n // The reconciliation decides which entries are seals; the store is read for every digest, a non-seal's matching nothing.\n const entries = claims.data.entries;\n const reconciliation = reconcileSigningLogSeals(\n entries,\n await this.store.findByDecisionPayloadDigests(entries.map((entry) => entry.decisionPayloadDigest)),\n // #2924: the seals destroyed with their record, read from the trail, so a deliberate destruction is not an alarm.\n await this.store.findDestroyedSealsByJti(entries.filter((entry) => entry.purpose === CryptoKey_Purpose.APPROVAL_SEAL).map((entry) => entry.jti)),\n );\n if (reconciliation.unaccounted.length > 0 || reconciliation.resigned.length > 0) {\n this.logger.error('[ApprovalSeals] The signing log records seal signatures the application does not store', {\n unaccounted: reconciliation.unaccounted, resigned: reconciliation.resigned, afterSequence: claims.data.afterSequence, headSequence: answer.headSequence,\n });\n }\n const body: ApprovalSealSigningLogExportAnswer = {\n jws: answer.jws,\n key: {\n kid: key.kid, publicJwk: key.publicJwk, alg: ES256_JWS_ALGORITHM, scope: key.scope, scopeId: key.scopeId, iss: key.iss, state: key.state,\n ...(key.compromiseInstant ? { compromiseInstant: key.compromiseInstant.toISOString() } : {}),\n },\n headSequence: answer.headSequence,\n throughSequence: answer.throughSequence,\n reconciliation,\n };\n res.json(body);\n }\n\n /**\n * Record an operator's act, or refuse it (#2903 review R1). The audit service writes an operator act with\n * `SURFACE_TO_CALLER`, so a row that cannot be written throws here; and a process with no audit service bound records\n * nothing, so it refuses too. A re-drive or an export whose row does not exist is an act nobody can account for.\n */\n private async recordOperatorAct(sealId: string, event: Parameters<AuditLogsBackendService['logApprovalSealEvent']>[0]): Promise<void> {\n const unrecorded = (reason: string) => {\n this.logger.error('[ApprovalSeals] An operator\\'s act on a seal could not be recorded, so it was refused', { sealId, eventType: event.eventType, reason });\n return this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'approval_seal_operator_act_unrecorded', sealId },\n });\n };\n if (!this.auditLogs) throw unrecorded('no audit service is bound');\n try {\n await this.auditLogs.logApprovalSealEvent(event);\n } catch (error) {\n throw unrecorded(describeCaughtError(error));\n }\n }\n\n /**\n * A seal hidden because its approver was erased (RETAIN_AND_IMPERSONALIZE): revealed through `readRetainedAsSystem`,\n * which records the reveal with the stated operator and reason and refuses one without them. Its person disclosures\n * were removed by the erasure, so its bundle verifies with them reported erased. Null when nobody is stated, or when\n * no retained seal has that id.\n */\n private async revealRetained(query: { sealId: string; operator?: string; justification?: string }): Promise<ApprovalSeal | null> {\n if (!this.systemAccess || !query.operator || !query.justification) return null;\n const row = await this.systemAccess.readRetainedAsSystem<Record<string, unknown>>(\n CoreResourceType.APPROVAL_SEALS as ResourceType, { _id: query.sealId },\n { accessingUserId: `operator:${query.operator}`, accessingUserRoles: [CORE_APP_ROLES.APP_ADMIN_SUPER_ADMIN], reason: query.justification },\n );\n return row ? ApprovalSealSchema.parse(row) : null;\n }\n\n /**\n * The seal's presentation, from its row or its attested file. Undefined when the seal has none;\n * {@link PRESENTATION_FILE_TAMPERED} when the file's bytes are not the ones the row recorded (review M10), and\n * {@link PRESENTATION_FILE_UNREADABLE} when the row names a file nothing can read (review F1). The export refuses both\n * rather than present the presentation as withheld.\n */\n private async presentationContentOf(\n seal: ApprovalSeal,\n ): Promise<Record<string, unknown> | typeof PRESENTATION_FILE_TAMPERED | typeof PRESENTATION_FILE_UNREADABLE | undefined> {\n const presentation = seal.presentation;\n if (!presentation) return undefined;\n if (presentation.content) return presentation.content;\n if (!presentation.file) return undefined;\n const unreadable = (reason: string): typeof PRESENTATION_FILE_UNREADABLE => {\n this.logger.error('[ApprovalSeals] A seal\\'s presentation file cannot be read; the export is refused', { sealId: seal._id, fileId: presentation.file!.fileId, reason });\n return PRESENTATION_FILE_UNREADABLE;\n };\n if (!this.files || !this.contexts) return unreadable('no file service is bound');\n const fileRow = await this.store.readFile(presentation.file.fileId);\n if (!fileRow) return unreadable('the file row is gone');\n let bytes: Buffer;\n try {\n bytes = await this.files.readFileBytes(fileRow as File, await this.contexts.createForInternalSystemOperation());\n } catch (error) {\n return unreadable(describeCaughtError(error));\n }\n if (createHash('sha256').update(bytes).digest('hex') !== presentation.file.contentSha256) {\n this.logger.error('[ApprovalSeals] A seal\\'s presentation file does not match the hash its row recorded; the export is refused', { sealId: seal._id, fileId: presentation.file.fileId });\n return PRESENTATION_FILE_TAMPERED;\n }\n return JSON.parse(bytes.toString('utf8')) as Record<string, unknown>;\n }\n\n /**\n * POST /manifested-copy/rebuild — rebuild the manifested copy of one SEALED seal's approved PDF (#2907): the door past\n * a copy the relay could not make (a word not yet generated, a store that failed) or one that is gone. Narrow: a live\n * SEALED seal with a file deliverable, whose copy is absent, gone, unlinked from it or no longer servable; never a seal\n * its approver's erasure retained (the copy would print an erased name). The copy is made exactly as the relay makes\n * it; the act is recorded, with its justification and the new copy's id, before the seal names it, and the copy is\n * discarded when the act cannot be recorded. The seal names it by the one write a SEALED seal takes after signing\n * (`replaceManifestedCopy`), guarded so two rebuilds cannot both land.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a manifested copy is rebuilt only from the attested original and the seal, through an audited, justified door.\n */\n private async handleRebuildManifestedCopy(req: Request, res: Response): Promise<void> {\n const body = RebuildCopyRequestSchema.parse(req.body);\n const seal = await this.store.read(body.sealId);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId: body.sealId },\n });\n }\n const conflict = (reason: string, detail?: string) => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason, sealId: body.sealId, status: seal.status, ...(detail ? { detail } : {}) },\n });\n if (seal.status !== ApprovalSealStatus.SEALED || !seal.jws) throw conflict('approval_seal_not_sealed');\n if (!seal.deliverableFileId) throw conflict('approval_seal_attests_no_file_deliverable');\n const deps = this.relay.manifestedCopyDependencies();\n if (!deps || !this.files) throw conflict('approval_seal_manifested_copy_unavailable', 'no file service is bound');\n const replacedFileId = manifestedCopyOfSeal(seal)?.fileId;\n if (replacedFileId && await this.copyIsPresent(seal._id, replacedFileId)) throw conflict('approval_seal_manifested_copy_present');\n\n // Made first, as an unlinked file nothing names (the files cleanup removes one left behind); recorded before the seal\n // names it, and discarded when the act cannot be recorded, so no copy is named that the trail does not account for.\n const outcome = await produceApprovalSealManifestedCopy(deps, seal, seal.jws);\n if (!('fileId' in outcome)) throw conflict(`approval_seal_manifested_copy_${outcome.skipped}`, outcome.detail);\n const context = await deps.internalContext();\n try {\n await this.recordOperatorAct(seal._id, approvalSealManifestedCopyRebuiltEvent(seal, {\n justification: body.justification, ...(body.operator ? { operator: body.operator } : {}), copyFileId: outcome.fileId,\n ...(replacedFileId ? { replacedCopyFileId: replacedFileId } : {}),\n }));\n } catch (error) {\n await this.files.deleteDetachedFile(outcome.fileId, context).catch(() => undefined);\n throw error;\n }\n const named = await this.store.replaceManifestedCopy(seal, outcome);\n if (!named) {\n await this.files.deleteDetachedFile(outcome.fileId, context).catch(() => undefined);\n throw conflict('approval_seal_moved_during_rebuild');\n }\n await this.files.linkFileToResource(outcome.fileId, CoreResourceType.APPROVAL_SEALS as ResourceType, seal._id, 'manifestedCopy', context, undefined, named);\n this.logger.info('[ApprovalSeals] An operator rebuilt a manifested copy', { sealId: seal._id, fileId: outcome.fileId, replacedFileId: replacedFileId ?? null });\n const result: ApprovalSealManifestedCopyRebuildResult = { sealId: seal._id, fileId: outcome.fileId, ...(replacedFileId ? { replacedFileId } : {}) };\n res.json(result);\n }\n\n /** Whether the copy a seal names still serves: its file row exists, is linked to this seal, and is servable. */\n private async copyIsPresent(sealId: string, fileId: string): Promise<boolean> {\n const row = await this.store.readFile(fileId);\n return !!row && row.linkedResourceId === sealId && FILE_DOWNLOADABLE_STATUSES.has(row.status as FileStatus);\n }\n\n /**\n * POST /retry — re-drive one DEAD seal, recorded with its justification, then ask the relay to sign it now. The relay's\n * answer is not awaited for the status: a seal it could not sign yet is the batch's, a minute later.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a refused seal is re-driven only through an audited, justified door, never rewritten.\n */\n private async handleRetry(req: Request, res: Response): Promise<void> {\n const body = RetryRequestSchema.parse(req.body);\n // A DEAD seal its approver's erasure froze is re-driven too (#3000): the re-drive writes only its relay fields.\n const seal = await this.store.readIncludingRetained(body.sealId);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND,\n context: { reason: 'approval_seal_not_found', sealId: body.sealId },\n });\n }\n const notDead = () => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT,\n context: { reason: 'approval_seal_not_dead', sealId: body.sealId, status: seal.status },\n });\n if (seal.status !== ApprovalSealStatus.DEAD) throw notDead();\n // Recorded BEFORE the seal moves, and a re-drive that cannot be recorded does not happen (#2903 review F6): the\n // justification is the control on this door, so a re-drive with no trail row is one nobody can account for.\n const justification = body.operator ? `${body.justification} (operator: ${body.operator})` : body.justification;\n await this.recordOperatorAct(seal._id, approvalSealRetriedEvent(seal, seal.lastRefusal ?? null, justification));\n const revived = await this.store.revive(seal);\n if (!revived) throw notDead();\n this.logger.info('[ApprovalSeals] A DEAD seal was re-driven by an operator', { sealId: seal._id, refusal: seal.lastRefusal ?? null });\n await this.relay.sealSoon([seal._id]);\n const after = await this.store.readIncludingRetained(seal._id);\n const result: ApprovalSealRetryResult = { sealId: seal._id, status: after?.status ?? revived.status, sealed: after?.status === ApprovalSealStatus.SEALED };\n res.json(result);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"approval-seals-admin.backend.controller.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-admin.backend.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ApprovalSealsAdminBackendController` — the operator's door onto DEAD approval seals (#2903).\n *\n * A seal goes DEAD only when the custodian refused it for a reason asking again cannot cure: the claims, the digest, the\n * tenant's key scope, the runtime's admission. That refusal is the DEFAULT and stays one; this door is the audited way\n * past it once the cause is fixed (a key re-scoped, a runtime re-admitted). The seal is re-driven, never rewritten: back\n * to PENDING with its claims and decision instant unchanged, so the verifier shows how late it was signed.\n *\n * Posture identical to the data-seeding and external-data operator doors: root-mounted under `/internal/admin/`, gated on\n * `APPLICATION_ADMIN_SECRET` (unset ⇒ 503), the secret compared as two SHA-256 digests with `timingSafeEqual`. The secret\n * names nobody, so the justification (required) and the operator's stated name are recorded as statements.\n */\nimport 'reflect-metadata';\nimport { timingSafeEqual } from 'crypto';\nimport { inject, injectable, optional } from 'inversify';\nimport type { NextFunction, Request, Response } from 'express';\nimport { z } from 'zod';\nimport { createHash } from 'crypto';\nimport {\n ApprovalSealSchema,\n ApprovalSealStatus,\n CORE_APP_ROLES,\n CoreResourceType,\n CryptoKey_Purpose,\n ErrorCustomMessageReference,\n ErrorType,\n FILE_DOWNLOADABLE_STATUSES,\n isWildoBackendError,\n type ApprovalSeal,\n type FileStatus,\n type ResourceType,\n} from '@wildo-ai/saas-models';\nimport {\n ApprovalSealDisclosureClaim,\n ES256_JWS_ALGORITHM,\n SigningLogExportClaimsSchema,\n SigningLogExportRequestSchema,\n type ApprovalSealDisclosureValue,\n type ApprovalSealVerificationBundle,\n} from '@wildo-ai/platform-config-lib';\n\nimport { AbstractControllerBackendService } from '~wildo-backend-lib/base-classes/services/controllers/abstract-controller.backend.service';\nimport { ErrorBuilderBackendService } from '~wildo-backend-lib/errors/services/error-builder.backend.service';\nimport { ErrorHandlerService } from '~wildo-backend-lib/errors/services/error-handler.backend.service';\nimport { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport type { AuditLogsBackendService } from '~wildo-backend-lib/compliance/audit-trails/services/audit-logs.backend.service';\nimport type { ApprovalSealRelayBackendService } from './approval-seal-relay.backend.service';\nimport { ApplicationSignatureRefusedError, type ApplicationKeysBackendService } from '../../security/crypto-keys/application-keys.backend.service';\nimport type { PublishedVerificationKeysBackendService } from '../../security/crypto-keys/published-verification-keys.backend.service';\nimport type { FilesBackendService } from '../../files/services/files.backend.service';\nimport type { ExecutionContextCreatorBackendService } from '../../runtime/services/execution-context-creator.backend.service';\nimport type { SystemAccessBackendService } from '../../base-classes/services/services/system-access.backend.service';\nimport { ApprovalSealRelayStore } from './approval-seal-relay-store.backend';\nimport { reconcileSigningLogSeals } from './signing-log-reconciliation.backend';\nimport { approvalSealExportedEvent, approvalSealManifestedCopyRebuiltEvent, approvalSealRetriedEvent } from './approval-seal-events.backend';\nimport { produceApprovalSealManifestedCopy } from './approval-seal-manifested-copy.backend';\nimport { assessApprovalSealCurrency } from './approval-seal-currency.backend';\nimport { ApprovalSealPresentationReadOutcome, readApprovalSealPresentation } from './approval-seal-presentation-read.backend';\nimport {\n ApprovalSealsAdminEndpointEnvKey,\n ApprovalSealsAdminEndpointHeaderKey,\n ApprovalSealsAdminEndpointMountBasePath,\n ApprovalSealsAdminEndpointPath,\n type ApprovalSealDeadListingEntry,\n type ApprovalSealCurrencyResult,\n type ApprovalSealManifestedCopyRebuildResult,\n type ApprovalSealRetryResult,\n type ApprovalSealSigningLogExportAnswer,\n} from './approval-seals-admin.backend.contracts';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\nconst ListDeadQuerySchema = z.object({ limit: z.coerce.number().int().min(1).max(200).optional() }).strict();\n\nconst BundleRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /**\n * For a seal RETAINED after its approver's erasure: who reveals it and why. A retained row is read only through the\n * audited reveal, which refuses one with no stated person or reason; a seal not retained ignores both.\n */\n operator: z.string().trim().min(1).max(200).optional(),\n justification: z.string().trim().min(1).max(2000).optional(),\n}).strict();\n\n/** What {@link ApprovalSealsAdminBackendController.presentationContentOf} answers for a file that is not what the row recorded. */\nconst PRESENTATION_FILE_TAMPERED = Symbol('presentation-file-tampered');\n/** A presentation file the row names and nothing can read: gone from the files store, or no file service bound (review F1). */\nconst PRESENTATION_FILE_UNREADABLE = Symbol('presentation-file-unreadable');\n\n/**\n * The claims a seal commits to whose content its row no longer holds (#2924). Nothing but an erasure removes a\n * disclosure from a seal's row: the approver's own erasure removes theirs, and a later erasure of the requester or of a\n * person the presentation named removes the requester's disclosure and rewrites the presentation (#3001). So a bundle\n * names them as ERASED, and the verifier reports UNDISCLOSED (erased) / PRESENTATION_ERASED instead of withheld.\n * Computed from the claims the seal SIGNED, so a claim the seal never made is never named.\n */\nexport function erasedClaimsOfSeal(seal: Pick<ApprovalSeal, 'claims'>, given: readonly ApprovalSealDisclosureValue[]): ApprovalSealDisclosureClaim[] {\n const claims = (seal.claims ?? {}) as { approver?: unknown; requester?: { disclosure?: unknown }; presentation?: { digest?: unknown }; statementDigest?: unknown };\n const committed: Array<[ApprovalSealDisclosureClaim, unknown]> = [\n [ApprovalSealDisclosureClaim.APPROVER, claims.approver],\n [ApprovalSealDisclosureClaim.REQUESTER, claims.requester?.disclosure],\n [ApprovalSealDisclosureClaim.PRESENTATION, claims.presentation?.digest],\n [ApprovalSealDisclosureClaim.STATEMENT, claims.statementDigest],\n ];\n const disclosed = new Set(given.map((disclosure) => disclosure.claimName));\n return committed.filter(([claimName, commitment]) => typeof commitment === 'string' && commitment.length > 0 && !disclosed.has(claimName)).map(([claimName]) => claimName);\n}\n\n/**\n * What a bundle says about the deliverable's manifested copy (#2907): its file while the seal still owns it; erased once\n * the approver's erasure removed it (the instant it was made survives, the reference does not); nothing when none was\n * made. Read from the row, so a copy the row no longer names is never reported as merely missing.\n */\nexport function manifestedCopyOfSeal(seal: Pick<ApprovalSeal, 'manifestedCopy' | 'manifestedCopyAt'>): ApprovalSealVerificationBundle['manifestedCopy'] {\n const copy = seal.manifestedCopy as unknown;\n const fileId = copy !== null && typeof copy === 'object' && 'fileId' in copy && typeof (copy as { fileId: unknown }).fileId === 'string'\n ? (copy as { fileId: string }).fileId\n : undefined;\n if (fileId) return { fileId };\n return seal.manifestedCopyAt ? { contentErased: true } : undefined;\n}\n\nconst RebuildCopyRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /** Why the copy is rebuilt (what was wrong, what changed): required, recorded on the trail. */\n justification: z.string().trim().min(1).max(2000),\n operator: z.string().trim().min(1).max(200).optional(),\n}).strict();\n\nconst CurrencyRequestSchema = z.object({ sealId: z.string().trim().min(1) }).strict();\n\nconst RetryRequestSchema = z.object({\n sealId: z.string().trim().min(1),\n /** Why the cause of the refusal is gone: required, recorded on the trail. */\n justification: z.string().trim().min(1).max(2000),\n /** Who is re-driving it, as the operator states it. The admin secret names nobody, so it is recorded as a statement. */\n operator: z.string().trim().min(1).max(200).optional(),\n}).strict();\n\n/** A compact JWS's payload, parsed; null when it is not JSON. */\nfunction decodedJwsPayloadOf(jws: string): unknown {\n try {\n return JSON.parse(Buffer.from(jws.split('.')[1] ?? '', 'base64url').toString('utf8'));\n } catch {\n return null;\n }\n}\n\n@injectable()\nexport class ApprovalSealsAdminBackendController extends AbstractControllerBackendService {\n private readonly store: ApprovalSealRelayStore;\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.ErrorBuilderService) errorBuilder: ErrorBuilderBackendService,\n @inject(SAAS_SERVICE_TYPES.ErrorHandlerService) errorHandler: ErrorHandlerService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService) repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n @inject(SAAS_SERVICE_TYPES.ApprovalSealRelayService) private readonly relay: Pick<ApprovalSealRelayBackendService, 'sealSoon' | 'manifestedCopyDependencies'>,\n @inject(SAAS_SERVICE_TYPES.AuditLogsService) @optional() private readonly auditLogs?: Pick<AuditLogsBackendService, 'logApprovalSealEvent'>,\n /** #2903 S7: the key records a bundle carries, from the application's own store. */\n @inject(SAAS_SERVICE_TYPES.PublishedVerificationKeysService) @optional()\n private readonly verificationKeys?: Pick<PublishedVerificationKeysBackendService, 'keyOf'>,\n /** #2903 S7: where a presentation moved to a frozen file is read back from. */\n @inject(SAAS_SERVICE_TYPES.FilesBackendService) @optional() private readonly files?: Pick<FilesBackendService, 'readFileBytes' | 'linkFileToResource' | 'deleteDetachedFile'>,\n @inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService) @optional()\n private readonly contexts?: Pick<ExecutionContextCreatorBackendService, 'createForInternalSystemOperation'>,\n /** #2903 S7: the audited reveal of a seal retained after its approver's erasure. */\n @inject(SAAS_SERVICE_TYPES.SystemAccessService) @optional() private readonly systemAccess?: Pick<SystemAccessBackendService, 'readRetainedAsSystem' | 'readAsSystem'>,\n /** #2932: the custodian's export of the signing log, relayed to the operator. */\n @inject(SAAS_SERVICE_TYPES.ApplicationKeysService) @optional() private readonly applicationKeys?: Pick<ApplicationKeysBackendService, 'exportSigningLog'>,\n ) {\n super(logger, errorBuilder, errorHandler, 'ApprovalSealsAdminController');\n this.store = new ApprovalSealRelayStore(repositoriesRegistry);\n }\n\n public getCustomMountBasePath(): string {\n return ApprovalSealsAdminEndpointMountBasePath;\n }\n\n protected setupRoutes(): void {\n this.router.use(this.requireAdminSecret.bind(this));\n this.router.get(ApprovalSealsAdminEndpointPath.DEAD, this.asyncHandler(this.handleListDead.bind(this)));\n this.logRouteRegistration('GET', ApprovalSealsAdminEndpointPath.DEAD, 'List the DEAD approval seals');\n this.router.post(ApprovalSealsAdminEndpointPath.RETRY, this.asyncHandler(this.handleRetry.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.RETRY, 'Re-drive one DEAD approval seal, justified');\n this.router.post(ApprovalSealsAdminEndpointPath.BUNDLE, this.asyncHandler(this.handleBundle.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.BUNDLE, 'The verification bundle of one SEALED approval seal, recorded');\n this.router.post(ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, this.asyncHandler(this.handleRebuildManifestedCopy.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.MANIFESTED_COPY_REBUILD, 'Rebuild one SEALED seal\\'s manifested copy, justified and recorded');\n this.router.post(ApprovalSealsAdminEndpointPath.CURRENCY, this.asyncHandler(this.handleCurrency.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.CURRENCY, 'Whether one seal\\'s approval is still current over the record it is about');\n this.router.post(ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, this.asyncHandler(this.handleSigningLogExport.bind(this)));\n this.logRouteRegistration('POST', ApprovalSealsAdminEndpointPath.SIGNING_LOG_EXPORT, 'An export of the signing log, signed by the custodian, for the operator to keep');\n }\n\n /** The guard of the sibling operator doors: both inputs hashed, so `timingSafeEqual` compares 32 bytes. */\n private requireAdminSecret(req: Request, _res: Response, next: NextFunction): void {\n const expectedSecret = process.env[ApprovalSealsAdminEndpointEnvKey];\n if (!expectedSecret) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE,\n context: { message: `approval-seal admin endpoints disabled (${ApprovalSealsAdminEndpointEnvKey} unset)`, envKey: ApprovalSealsAdminEndpointEnvKey },\n });\n }\n const providedSecret = req.headers[ApprovalSealsAdminEndpointHeaderKey.toLowerCase()] as string | undefined;\n if (!providedSecret) {\n throw this.errorBuilder.buildError(ErrorType.AUTHENTICATION, undefined, {\n customMessageReference: ErrorCustomMessageReference.SEED_ADMIN_SECRET_REQUIRED,\n context: { headerKey: ApprovalSealsAdminEndpointHeaderKey },\n });\n }\n if (!timingSafeEqual(createHash('sha256').update(providedSecret).digest(), createHash('sha256').update(expectedSecret).digest())) {\n throw this.errorBuilder.buildError(ErrorType.AUTHENTICATION, undefined, {\n customMessageReference: ErrorCustomMessageReference.SEED_ADMIN_SECRET_INVALID,\n context: { headerKey: ApprovalSealsAdminEndpointHeaderKey },\n });\n }\n next();\n }\n\n /** GET /dead — the DEAD seals, oldest death first. */\n private async handleListDead(req: Request, res: Response): Promise<void> {\n const query = ListDeadQuerySchema.parse(req.query);\n const dead = await this.store.listDead(query.limit ?? 50);\n const seals: ApprovalSealDeadListingEntry[] = dead.map((seal) => ({\n sealId: seal._id, status: seal.status, source: seal.source, decisionRef: seal.decisionRef, step: seal.step, sequence: seal.sequence,\n organizationId: seal.organizationId ?? null, linkedResourceType: seal.linkedResourceType, linkedResourceId: seal.linkedResourceId,\n decidedAt: seal.decidedAt.toISOString(), deadAt: seal.deadAt?.toISOString() ?? null, attemptCount: seal.attemptCount, lastRefusal: seal.lastRefusal ?? null,\n }));\n res.json({ seals });\n }\n\n /**\n * POST /bundle — what an auditor needs to verify one seal offline (#2903 S7): the JWS, its key's stored record, the issuer,\n * and every disclosure the row still holds. A person erased since keeps the seal and gives no disclosure: the bundle\n * names what the row lost (`erasedClaims`, #2924), which the verifier reports as UNDISCLOSED (erased), or for a\n * presentation an erasure rewrote, PRESENTATION_ERASED. A presentation moved to a frozen file is read back and checked against the hash the row\n * recorded before it is handed out as the presentation. A file deliverable the seal attests (#2904) is named with its\n * salt, so a verifier given the file's bytes checks them against the presentation's commitment.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a seal is exported with the key record and disclosures that verify it offline, never re-derived.\n */\n private async handleBundle(req: Request, res: Response): Promise<void> {\n const request = BundleRequestSchema.parse(req.body);\n const { sealId } = request;\n const live = await this.store.read(sealId);\n const seal = live ?? await this.revealRetained(request);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId },\n });\n }\n const conflict = (reason: string) => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason, sealId, status: seal.status },\n });\n if (seal.status !== ApprovalSealStatus.SEALED || !seal.jws || !seal.kid || !seal.iss) throw conflict('approval_seal_not_sealed');\n const key = await this.verificationKeys?.keyOf(seal.kid);\n if (!key) throw conflict('approval_seal_key_record_not_held');\n const disclosures: ApprovalSealDisclosureValue[] = [...(seal.disclosures ?? []), ...(seal.requesterDisclosure ? [seal.requesterDisclosure] : [])].map((disclosure) => ({\n claimName: disclosure.claimName as ApprovalSealDisclosureClaim, salt: disclosure.salt, value: disclosure.value,\n }));\n const presentationContent = await this.presentationContentOf(seal);\n if (presentationContent === PRESENTATION_FILE_TAMPERED) throw conflict('approval_seal_presentation_file_mismatch');\n // Never a bundle without the presentation its seal commits to: the verifier would report it \"not given\", which reads\n // as a choice rather than a loss (#2903 review F1).\n if (presentationContent === PRESENTATION_FILE_UNREADABLE) throw conflict('approval_seal_presentation_file_unreadable');\n if (seal.presentation && presentationContent) {\n disclosures.push({ claimName: ApprovalSealDisclosureClaim.PRESENTATION, salt: seal.presentation.salt, value: presentationContent });\n }\n if (seal.statement) disclosures.push({ claimName: ApprovalSealDisclosureClaim.STATEMENT, salt: seal.statement.salt, value: seal.statement.content });\n const erasedClaims = erasedClaimsOfSeal(seal, disclosures);\n const manifestedCopy = await this.withRecordedCopyHash(manifestedCopyOfSeal(seal));\n const bundle: ApprovalSealVerificationBundle = {\n jws: seal.jws,\n keys: [{\n kid: key.kid, publicJwk: key.publicJwk, alg: ES256_JWS_ALGORITHM, scope: key.scope, scopeId: key.scopeId, iss: key.iss, state: key.state,\n ...(key.compromiseInstant ? { compromiseInstant: key.compromiseInstant.toISOString() } : {}),\n }],\n expectedIssuer: seal.iss,\n disclosures,\n // #2924: what the seal commits to and the row no longer holds, which only an erasure removes, so the verifier\n // reports it UNDISCLOSED (erased) / PRESENTATION_ERASED rather than withheld.\n ...(erasedClaims.length > 0 ? { erasedClaims } : {}),\n // #2904: the file deliverable the seal attests, with the salt that opens its commitment. Not its bytes: the auditor\n // takes those from the file itself (`wildo seal verify --deliverable`), so a file changed since is reported.\n ...(seal.deliverableFileId && seal.deliverableFileSalt ? { deliverableFile: { fileId: seal.deliverableFileId, salt: seal.deliverableFileSalt } } : {}),\n // #2907: the deliverable's manifested copy, or the statement that the approver's erasure deleted it. Not its bytes,\n // which the auditor takes from the file (`wildo seal verify --manifested-copy`).\n ...(manifestedCopy ? { manifestedCopy } : {}),\n };\n // Recorded before anything leaves: the bundle hands the people the seal names to an operator (review L6), and an\n // export that cannot be recorded is refused (review R1).\n await this.recordOperatorAct(seal._id, approvalSealExportedEvent(seal, {\n disclosedClaims: disclosures.map((disclosure) => disclosure.claimName), retained: live === null,\n ...(request.operator ? { operator: request.operator } : {}), ...(request.justification ? { justification: request.justification } : {}),\n }));\n res.json(bundle);\n }\n\n /**\n * POST /currency — whether one seal's approval is still current over the record it is about (#2914): the values of the\n * fields its tool declared the approval covers, as its presentation carries them, against the record as it is now\n * (`assessApprovalSealCurrency`). Any seal status: a PENDING seal's presentation is already what the person approved.\n * Names which sealed fields moved and never a value, so nothing is recorded on the trail.\n *\n * assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — an operator can ask whether a sealed approval still describes its record, computed from the seal and the record, never assumed.\n */\n private async handleCurrency(req: Request, res: Response): Promise<void> {\n const { sealId } = CurrencyRequestSchema.parse(req.body);\n const seal = await this.store.read(sealId);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId },\n });\n }\n const presentationContent = await this.presentationContentOf(seal);\n if (presentationContent === PRESENTATION_FILE_TAMPERED || presentationContent === PRESENTATION_FILE_UNREADABLE) {\n throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT,\n context: { reason: presentationContent === PRESENTATION_FILE_TAMPERED ? 'approval_seal_presentation_file_mismatch' : 'approval_seal_presentation_file_unreadable', sealId },\n });\n }\n const systemAccess = this.systemAccess;\n const assessment = await assessApprovalSealCurrency(seal, presentationContent, {\n readRecord: async (resourceType, resourceId) => {\n if (!systemAccess) {\n throw this.errorBuilder.buildError(ErrorType.CONFIGURATION, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFIGURATION, context: { message: 'The approval-seal currency check reads the record through the system access service.' },\n });\n }\n return systemAccess.readAsSystem<Record<string, unknown>>(resourceType as ResourceType, { _id: resourceId }, { suppressSystemAudit: true } as never)\n .catch((error: unknown) => {\n if (isWildoBackendError(error) && error.type === ErrorType.NOT_FOUND) return null;\n throw error;\n });\n },\n });\n const result: ApprovalSealCurrencyResult = { sealId, ...assessment };\n res.json(result);\n }\n\n /**\n * The copy's SHA-256 as its file row recorded it when it was written (#2907): the host's statement that binds what the\n * verifier takes from the copy itself (its words, its verification address). A copy whose row is gone is named without\n * it, and the verifier still rebuilds it against the seal.\n */\n private async withRecordedCopyHash(copy: ApprovalSealVerificationBundle['manifestedCopy']): Promise<ApprovalSealVerificationBundle['manifestedCopy']> {\n if (!copy?.fileId) return copy;\n const row = await this.store.readFile(copy.fileId).catch(() => null);\n const contentSha256 = typeof row?.contentSha256 === 'string' ? row.contentSha256 : undefined;\n return contentSha256 ? { ...copy, contentSha256 } : copy;\n }\n\n /**\n * POST /signing-log/export — the custodian's export of the application's signing log (#2932), with the stored record of\n * the key that signed it, for the operator to keep where the host cannot rewrite it. The custodian builds and signs the\n * export from its own log and records its signature there (and on the platform's trail), so the next export lists\n * this one; the export names no person, so this door records nothing more. Its key record comes from the application's\n * own store, which a verifier given the operator's pin set uses only for the public half and the issuer.\n *\n * Each seal signature the export carries is reconciled with the stored seals, here, where the seals are: a signature\n * no stored seal accounts for is reported in the answer and logged as an error. Reconciled at the pull, each entry is\n * reconciled once, when the operator takes it.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — the operator holds signed, hash-chained exports of the signing log outside the host (#2932)\n */\n private async handleSigningLogExport(req: Request, res: Response): Promise<void> {\n const request = SigningLogExportRequestSchema.parse(req.body);\n if (!this.applicationKeys) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'signing_log_export_unavailable', message: 'no application keys service is bound' },\n });\n }\n let answer;\n try {\n answer = await this.applicationKeys.exportSigningLog(request);\n } catch (error) {\n if (!(error instanceof ApplicationSignatureRefusedError)) throw error;\n // KEY_CUSTODIAN_REFUSED admits `code` and `retryable` past the client sanitizer, so the operator reads the refusal.\n throw this.errorBuilder.buildError(error.retryable ? ErrorType.INFRASTRUCTURE : ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.KEY_CUSTODIAN_REFUSED,\n context: { code: error.refusal, retryable: error.retryable, message: error.message },\n });\n }\n const key = await this.verificationKeys?.keyOf(answer.kid);\n if (!key || key.purpose !== CryptoKey_Purpose.SIGNING_LOG_EXPORT) {\n throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason: 'signing_log_export_key_record_not_held', kid: answer.kid },\n });\n }\n const claims = SigningLogExportClaimsSchema.safeParse(decodedJwsPayloadOf(answer.jws));\n if (!claims.success) {\n throw this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'signing_log_export_unreadable', kid: answer.kid },\n });\n }\n // The reconciliation decides which entries are seals; the store is read for every digest, a non-seal's matching nothing.\n const entries = claims.data.entries;\n const reconciliation = reconcileSigningLogSeals(\n entries,\n await this.store.findByDecisionPayloadDigests(entries.map((entry) => entry.decisionPayloadDigest)),\n // #2924: the seals destroyed with their record, read from the trail, so a deliberate destruction is not an alarm.\n await this.store.findDestroyedSealsByJti(entries.filter((entry) => entry.purpose === CryptoKey_Purpose.APPROVAL_SEAL).map((entry) => entry.jti)),\n );\n if (reconciliation.unaccounted.length > 0 || reconciliation.resigned.length > 0) {\n this.logger.error('[ApprovalSeals] The signing log records seal signatures the application does not store', {\n unaccounted: reconciliation.unaccounted, resigned: reconciliation.resigned, afterSequence: claims.data.afterSequence, headSequence: answer.headSequence,\n });\n }\n const body: ApprovalSealSigningLogExportAnswer = {\n jws: answer.jws,\n key: {\n kid: key.kid, publicJwk: key.publicJwk, alg: ES256_JWS_ALGORITHM, scope: key.scope, scopeId: key.scopeId, iss: key.iss, state: key.state,\n ...(key.compromiseInstant ? { compromiseInstant: key.compromiseInstant.toISOString() } : {}),\n },\n headSequence: answer.headSequence,\n throughSequence: answer.throughSequence,\n reconciliation,\n };\n res.json(body);\n }\n\n /**\n * Record an operator's act, or refuse it (#2903 review R1). The audit service writes an operator act with\n * `SURFACE_TO_CALLER`, so a row that cannot be written throws here; and a process with no audit service bound records\n * nothing, so it refuses too. A re-drive or an export whose row does not exist is an act nobody can account for.\n */\n private async recordOperatorAct(sealId: string, event: Parameters<AuditLogsBackendService['logApprovalSealEvent']>[0]): Promise<void> {\n const unrecorded = (reason: string) => {\n this.logger.error('[ApprovalSeals] An operator\\'s act on a seal could not be recorded, so it was refused', { sealId, eventType: event.eventType, reason });\n return this.errorBuilder.buildError(ErrorType.INFRASTRUCTURE, undefined, {\n customMessageReference: ErrorCustomMessageReference.INFRASTRUCTURE, context: { reason: 'approval_seal_operator_act_unrecorded', sealId },\n });\n };\n if (!this.auditLogs) throw unrecorded('no audit service is bound');\n try {\n await this.auditLogs.logApprovalSealEvent(event);\n } catch (error) {\n throw unrecorded(describeCaughtError(error));\n }\n }\n\n /**\n * A seal hidden because its approver was erased (RETAIN_AND_IMPERSONALIZE): revealed through `readRetainedAsSystem`,\n * which records the reveal with the stated operator and reason and refuses one without them. Its person disclosures\n * were removed by the erasure, so its bundle verifies with them reported erased. Null when nobody is stated, or when\n * no retained seal has that id.\n */\n private async revealRetained(query: { sealId: string; operator?: string; justification?: string }): Promise<ApprovalSeal | null> {\n if (!this.systemAccess || !query.operator || !query.justification) return null;\n const row = await this.systemAccess.readRetainedAsSystem<Record<string, unknown>>(\n CoreResourceType.APPROVAL_SEALS as ResourceType, { _id: query.sealId },\n { accessingUserId: `operator:${query.operator}`, accessingUserRoles: [CORE_APP_ROLES.APP_ADMIN_SUPER_ADMIN], reason: query.justification },\n );\n return row ? ApprovalSealSchema.parse(row) : null;\n }\n\n /**\n * The seal's presentation, from its row or its attested file. Undefined when the seal has none;\n * {@link PRESENTATION_FILE_TAMPERED} when the file's bytes are not the ones the row recorded (review M10), and\n * {@link PRESENTATION_FILE_UNREADABLE} when the row names a file nothing can read (review F1). The export refuses both\n * rather than present the presentation as withheld.\n */\n private async presentationContentOf(\n seal: ApprovalSeal,\n ): Promise<Record<string, unknown> | typeof PRESENTATION_FILE_TAMPERED | typeof PRESENTATION_FILE_UNREADABLE | undefined> {\n const read = await readApprovalSealPresentation(seal, { readFileRow: (fileId) => this.store.readFile(fileId), files: this.files, contexts: this.contexts });\n switch (read.outcome) {\n case ApprovalSealPresentationReadOutcome.CONTENT:\n return read.content;\n case ApprovalSealPresentationReadOutcome.NONE:\n return undefined;\n case ApprovalSealPresentationReadOutcome.TAMPERED:\n this.logger.error('[ApprovalSeals] A seal\\'s presentation file does not match the hash its row recorded; the export is refused', { sealId: seal._id, fileId: read.fileId });\n return PRESENTATION_FILE_TAMPERED;\n case ApprovalSealPresentationReadOutcome.UNREADABLE:\n this.logger.error('[ApprovalSeals] A seal\\'s presentation file cannot be read; the export is refused', { sealId: seal._id, fileId: read.fileId, reason: read.reason });\n return PRESENTATION_FILE_UNREADABLE;\n }\n }\n\n /**\n * POST /manifested-copy/rebuild — rebuild the manifested copy of one SEALED seal's approved PDF (#2907): the door past\n * a copy the relay could not make (a word not yet generated, a store that failed) or one that is gone. Narrow: a live\n * SEALED seal with a file deliverable, whose copy is absent, gone, unlinked from it or no longer servable; never a seal\n * its approver's erasure retained (the copy would print an erased name). The copy is made exactly as the relay makes\n * it; the act is recorded, with its justification and the new copy's id, before the seal names it, and the copy is\n * discarded when the act cannot be recorded. The seal names it by the one write a SEALED seal takes after signing\n * (`replaceManifestedCopy`), guarded so two rebuilds cannot both land.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a manifested copy is rebuilt only from the attested original and the seal, through an audited, justified door.\n */\n private async handleRebuildManifestedCopy(req: Request, res: Response): Promise<void> {\n const body = RebuildCopyRequestSchema.parse(req.body);\n const seal = await this.store.read(body.sealId);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_not_found', sealId: body.sealId },\n });\n }\n const conflict = (reason: string, detail?: string) => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason, sealId: body.sealId, status: seal.status, ...(detail ? { detail } : {}) },\n });\n if (seal.status !== ApprovalSealStatus.SEALED || !seal.jws) throw conflict('approval_seal_not_sealed');\n if (!seal.deliverableFileId) throw conflict('approval_seal_attests_no_file_deliverable');\n const deps = this.relay.manifestedCopyDependencies();\n if (!deps || !this.files) throw conflict('approval_seal_manifested_copy_unavailable', 'no file service is bound');\n const replacedFileId = manifestedCopyOfSeal(seal)?.fileId;\n if (replacedFileId && await this.copyIsPresent(seal._id, replacedFileId)) throw conflict('approval_seal_manifested_copy_present');\n\n // Made first, as an unlinked file nothing names (the files cleanup removes one left behind); recorded before the seal\n // names it, and discarded when the act cannot be recorded, so no copy is named that the trail does not account for.\n const outcome = await produceApprovalSealManifestedCopy(deps, seal, seal.jws);\n if (!('fileId' in outcome)) throw conflict(`approval_seal_manifested_copy_${outcome.skipped}`, outcome.detail);\n const context = await deps.internalContext();\n try {\n await this.recordOperatorAct(seal._id, approvalSealManifestedCopyRebuiltEvent(seal, {\n justification: body.justification, ...(body.operator ? { operator: body.operator } : {}), copyFileId: outcome.fileId,\n ...(replacedFileId ? { replacedCopyFileId: replacedFileId } : {}),\n }));\n } catch (error) {\n await this.files.deleteDetachedFile(outcome.fileId, context).catch(() => undefined);\n throw error;\n }\n const named = await this.store.replaceManifestedCopy(seal, outcome);\n if (!named) {\n await this.files.deleteDetachedFile(outcome.fileId, context).catch(() => undefined);\n throw conflict('approval_seal_moved_during_rebuild');\n }\n await this.files.linkFileToResource(outcome.fileId, CoreResourceType.APPROVAL_SEALS as ResourceType, seal._id, 'manifestedCopy', context, undefined, named);\n this.logger.info('[ApprovalSeals] An operator rebuilt a manifested copy', { sealId: seal._id, fileId: outcome.fileId, replacedFileId: replacedFileId ?? null });\n const result: ApprovalSealManifestedCopyRebuildResult = { sealId: seal._id, fileId: outcome.fileId, ...(replacedFileId ? { replacedFileId } : {}) };\n res.json(result);\n }\n\n /** Whether the copy a seal names still serves: its file row exists, is linked to this seal, and is servable. */\n private async copyIsPresent(sealId: string, fileId: string): Promise<boolean> {\n const row = await this.store.readFile(fileId);\n return !!row && row.linkedResourceId === sealId && FILE_DOWNLOADABLE_STATUSES.has(row.status as FileStatus);\n }\n\n /**\n * POST /retry — re-drive one DEAD seal, recorded with its justification, then ask the relay to sign it now. The relay's\n * answer is not awaited for the status: a seal it could not sign yet is the batch's, a minute later.\n *\n * assurance-control: WILDO.AUDIT.EVIDENCE_INTEGRITY — a refused seal is re-driven only through an audited, justified door, never rewritten.\n */\n private async handleRetry(req: Request, res: Response): Promise<void> {\n const body = RetryRequestSchema.parse(req.body);\n // A DEAD seal its approver's erasure froze is re-driven too (#3000): the re-drive writes only its relay fields.\n const seal = await this.store.readIncludingRetained(body.sealId);\n if (!seal) {\n throw this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND,\n context: { reason: 'approval_seal_not_found', sealId: body.sealId },\n });\n }\n const notDead = () => this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT,\n context: { reason: 'approval_seal_not_dead', sealId: body.sealId, status: seal.status },\n });\n if (seal.status !== ApprovalSealStatus.DEAD) throw notDead();\n // Recorded BEFORE the seal moves, and a re-drive that cannot be recorded does not happen (#2903 review F6): the\n // justification is the control on this door, so a re-drive with no trail row is one nobody can account for.\n const justification = body.operator ? `${body.justification} (operator: ${body.operator})` : body.justification;\n await this.recordOperatorAct(seal._id, approvalSealRetriedEvent(seal, seal.lastRefusal ?? null, justification));\n const revived = await this.store.revive(seal);\n if (!revived) throw notDead();\n this.logger.info('[ApprovalSeals] A DEAD seal was re-driven by an operator', { sealId: seal._id, refusal: seal.lastRefusal ?? null });\n await this.relay.sealSoon([seal._id]);\n const after = await this.store.readIncludingRetained(seal._id);\n const result: ApprovalSealRetryResult = { sealId: seal._id, status: after?.status ?? revived.status, sealed: after?.status === ApprovalSealStatus.SEALED };\n res.json(result);\n }\n}\n"]}
|