Refine function-bound postbox architecture

This commit is contained in:
2026-07-28 19:05:41 +02:00
parent 5d141a0eaa
commit 19216e5043
3 changed files with 163 additions and 48 deletions

View File

@@ -2,9 +2,9 @@
## Scope ## Scope
This repository owns the `postbox` module: in-platform postboxes, role-organization-bound access, postbox messages, postbox directory APIs, internal and portal postbox surfaces, campaign postbox integration, postbox-owned migrations, and future `@govoplan/postbox-webui`. This repository owns the `postbox` module: in-platform postboxes, function-organization-bound access, postbox messages, postbox directory APIs, internal and portal postbox surfaces, campaign postbox integration, postbox-owned migrations, and future `@govoplan/postbox-webui`.
Postboxes are not login-bound mailboxes. They are platform-owned communication and access containers whose visibility is derived from organizational role assignments, explicit postbox bindings, and capability contracts. Postboxes are not login-bound mailboxes. They are platform-owned communication and access containers whose visibility is derived primarily from effective identity-to-organization-function assignments, explicit postbox bindings, and capability contracts.
## Local Commands ## Local Commands
@@ -25,7 +25,8 @@ tools/checks/check-focused.sh
## Working Rules ## Working Rules
- Keep postbox behavior in this module, not core. - Keep postbox behavior in this module, not core.
- Derive role-organization access through core/access contracts; do not duplicate RBAC membership logic locally. - Resolve units and functions through Organizations and effective identity-to-function assignments through IDM. Use Core/Access for generic Postbox action authorization; do not turn a function assignment into an RBAC role merely to open its function postbox.
- Do not duplicate identity, organization, assignment, hierarchy, or RBAC logic locally.
- Do not import mail, files, campaign, or portal internals. Use manifests, capabilities, events, API routes, and typed DTOs. - Do not import mail, files, campaign, or portal internals. Use manifests, capabilities, events, API routes, and typed DTOs.
- Treat postbox access changes as auditable security events, especially when access changes because a role assignment changes. - Treat postbox access changes as auditable security events, especially when access changes because a function assignment, delegation, acting context, or generic Postbox permission changes.
- Keep campaign delivery, file evidence, and portal usage optional behind capability boundaries. - Keep campaign delivery, file evidence, and portal usage optional behind capability boundaries.

View File

@@ -4,7 +4,8 @@
**Repository type:** module (domain). **Repository type:** module (domain).
<!-- govoplan-repository-type:end --> <!-- govoplan-repository-type:end -->
GovOPlaN Postbox provides platform-owned postboxes for internal work, portals, campaign flows, and role-bound organizational communication. GovOPlaN Postbox provides platform-owned postboxes for internal work, portals,
campaign flows, and function-bound organizational communication.
## Ownership ## Ownership
@@ -13,21 +14,28 @@ This repository owns:
- backend module manifest `postbox` - backend module manifest `postbox`
- postbox permissions and policy checks - postbox permissions and policy checks
- postbox, binding, message, participant, attachment-reference, and audit-facing data models - postbox, binding, message, participant, attachment-reference, and audit-facing data models
- role-organization-bound access resolution for postboxes - function-organization-bound access resolution for postboxes through
normalized Identity, IDM, Organizations, and Core/Access contracts
- API routes for postbox directory, messages, access checks, and administration - API routes for postbox directory, messages, access checks, and administration
- optional integration capabilities for campaign, files, portal, notification, and mail-facing workflows - optional integration capabilities for campaign, files, portal, notification, and mail-facing workflows
- future WebUI package `@govoplan/postbox-webui` - future WebUI package `@govoplan/postbox-webui`
Core owns auth, tenants, RBAC evaluation, database/session primitives, module discovery, migrations, CSRF/API helpers, and shell layout. Access owns identities, users, groups, roles, memberships, and administrative RBAC surfaces. Core owns auth, tenants, RBAC evaluation, database/session primitives, module
discovery, migrations, CSRF/API helpers, and shell layout. Identity owns
identities and account links. Organizations owns units, structures, function
types, and concrete functions. IDM owns effective identity-to-function
assignments, delegation, and acting-for facts. Access owns generic application
roles, permissions, and administrative RBAC surfaces.
## Role-bound postboxes ## Function-bound postboxes
A function-bound postbox is linked to an organizational unit and one or more A function-bound postbox has a stable institutional address linked to an
institutional functions. It can exist with zero, one, or several current organizational unit and one or more functions. It can exist with zero, one, or
incumbents. A person can access it only while their identity has an effective several current incumbents. A person can access it only while their identity
assignment or time-bounded delegation in that organizational context. Access has an effective assignment or time-bounded delegation in that organizational
is not tied to a login mailbox, personal email address, or static user context and their account may perform the relevant Postbox action. Access is
assignment. not tied to a login mailbox, personal email address, static user assignment, or
function-to-RBAC-role mapping.
When the assignment changes, postbox access and encrypted key grants change When the assignment changes, postbox access and encrypted key grants change
with it. The postbox keeps durable content and evidence history through with it. The postbox keeps durable content and evidence history through
@@ -35,6 +43,19 @@ vacancy, hand-over, and delegation; multiple incumbents receive independently
auditable access. Revocation prevents future platform key access but cannot auditable access. Revocation prevents future platform key access but cannot
erase plaintext already fetched or exported. erase plaintext already fetched or exported.
Reusable Postbox templates may target a function type and organization scope.
Unit-specific addresses are resolved lazily and remain stable through vacancy
and reassignment. Exact postboxes remain available for exceptional
responsibilities or case/service contexts.
Users holding several functions may group selected postboxes into unified
inbox views. These are query projections only: messages, address, read state,
retention, and evidence remain attached to their source postboxes.
Hierarchy propagation is off by default. Explicit copy, attention/escalation,
and shared-visibility rules are distinct, bounded, classification-aware, and
snapshotted when a message is delivered.
## Module integration ## Module integration
Backend entry point: Backend entry point:

View File

@@ -19,23 +19,26 @@ manifests, external-recipient tokens, or honest retraction semantics. The
cross-module target architecture is recorded in cross-module target architecture is recorded in
`govoplan-core/docs/POSTBOX_E2EE_ARCHITECTURE.md`. `govoplan-core/docs/POSTBOX_E2EE_ARCHITECTURE.md`.
## Function/Role-Organization-Bound Access ## Function-Organization-Bound Access
The special access pattern is a postbox linked to an organizational unit and The primary access pattern is a postbox linked to an organizational unit and
one or more roles or functions. A person can access that postbox while their one or more institutional functions. A person can access that postbox while
account has an effective matching function assignment in that organizational their identity/account has an effective matching function assignment in that
unit, or while a matching function maps to one of the required roles. organizational unit and their account may perform the requested Postbox action.
Opening a function postbox does not require mapping the function to an RBAC
role.
Example: Example:
- Organizational unit: `District Office North` - Organizational unit: `District Office North`
- Required role: `Case Clerk` - Required function: `Case Clerk`
- Postbox: `District Office North / Case Clerk Intake` - Postbox: `District Office North / Case Clerk Intake`
Any identity/account currently holding the `Case Clerk` function or a mapped Any identity/account currently holding the `Case Clerk` function for `District
role for `District Office North` can see the postbox. When the function, Office North` can see the postbox if it also satisfies the generic Postbox
delegation, or role mapping is removed or expires, access disappears without permission and applicable policy. When the function assignment or delegation
moving messages or reassigning a mailbox. is removed or expires, access disappears without moving messages or
reassigning a mailbox.
The postbox exists independently of its holders. It remains addressable while The postbox exists independently of its holders. It remains addressable while
the function is vacant and may accept durable deliveries according to policy; the function is vacant and may accept durable deliveries according to policy;
@@ -47,10 +50,10 @@ its history.
This makes postboxes useful for responsibilities that outlive individuals: This makes postboxes useful for responsibilities that outlive individuals:
- intake desks - intake desks
- role-based service queues - function-based service queues
- campaign sender or response desks - campaign sender or response desks
- portal message inboxes for organizational responsibilities - portal message inboxes for organizational responsibilities
- file or evidence drops linked to a role in an organization - file or evidence drops linked to a function in an organization
### Incumbency, hand-over, and delegation ### Incumbency, hand-over, and delegation
@@ -70,19 +73,33 @@ This makes postboxes useful for responsibilities that outlive individuals:
replacement, or new version is a new linked object with provenance while the replacement, or new version is a new linked object with provenance while the
previous signed/ciphertext manifest remains governed by retention policy. previous signed/ciphertext manifest remains governed by retention policy.
## Authorization Model ## Directory And Authorization Model
Postbox authorization should be derived from access-owned identity and role data through core/access contracts. The postbox module stores postbox bindings and postbox-specific permissions, but it should not duplicate membership, group, or role resolution. The normalized ownership boundary is:
- Organizations owns units, structures, function types, and concrete
functions.
- Identity owns identities and account links.
- IDM owns effective identity-to-function assignments, validity, delegation,
acting-for context, and assignment lifecycle facts.
- Core/Access authorizes generic Postbox actions.
- Postbox owns templates, stable addresses, containers, messages, routing,
visibility decisions, grouping preferences, and retention.
The Postbox module stores postbox bindings and postbox-specific decisions, but
it must not duplicate identity, organization, assignment, hierarchy, or RBAC
resolution.
The minimum authorization inputs are: The minimum authorization inputs are:
- postbox id - postbox id
- tenant id - tenant id
- organizational unit id - organizational unit id
- required function id, role id, or role key - required function id or function type id
- actor identity id - actor identity id
- current effective function assignments, delegations, and roles from the - current effective function assignments, delegations, and acting context from
access module IDM
- generic Postbox permissions from Core/Access
- optional explicit administrative grants for postbox administration - optional explicit administrative grants for postbox administration
The expected result is a narrow access decision: The expected result is a narrow access decision:
@@ -93,27 +110,100 @@ The expected result is a narrow access decision:
- can attach or link files - can attach or link files
- can administer bindings - can administer bindings
Access changes must be auditable because a person can gain or lose postbox visibility through role assignment changes rather than direct postbox membership edits. Access changes must be auditable because a person can gain or lose postbox
visibility through function-assignment changes rather than direct postbox
membership edits.
Runtime integration must use the access kernel capabilities: Runtime integration must use the kernel capabilities:
- `access.semanticDirectory` to inspect identity/account/function facts. - `identity.directory` to resolve identities and account links.
- `access.explanation` to attach identity/account/function/role/right - `idm.directory` to resolve effective function assignments.
provenance to access decisions. - `organizations.directory` to resolve function, function-type, unit, and
hierarchy facts.
- the Core/Access permission evaluator for generic Postbox actions.
- access explanation/audit contracts to attach permission and acting-context
provenance where available.
Postbox must not import access ORM models or duplicate function/role Postbox must not import Identity, IDM, Organizations, or Access ORM models.
resolution. Acting-in-place access should require an explicit selected acting Acting-in-place access requires an explicit selected acting context. A function
context once Access exposes that runtime selector. assignment is an organizational responsibility fact; it does not grant
unrelated application permissions.
## Templates And Stable Addresses
A reusable Postbox template can target a function type and an organization
scope, such as a unit type, structure, or subtree. Postbox resolves a stable
unit-specific address from the tenant, template revision, concrete unit,
concrete function, and optional case/service context.
Addresses should be resolved lazily and idempotently rather than eagerly
creating empty containers for every unit. They remain durable through vacancy
and reassignment. A delivery snapshots the template revision and normalized
organization/function references so later hierarchy changes do not rewrite
history.
Exact postboxes remain useful for exceptional responsibilities that do not
belong to a reusable function type.
## Unified Inbox Projections
A user with several functions can group selected visible postboxes into named
unified inbox views and keep other responsibilities separate. Grouping is a
query projection only. It never merges source containers, messages, read or
acknowledgement state, retention, encryption keys, or audit evidence.
Every item and action continues to show the source function, unit, postbox,
assignment/delegation context, and classification. Policy may require some
postboxes to remain separate.
## Hierarchy Routing
Hierarchy behavior is disabled by default. The system distinguishes:
- a linked copy delivered to a parent function postbox
- attention or escalation metadata sent to a parent responsibility
- shared visibility over the original message
These have different privacy, retention, acknowledgement, and audit effects
and must not be treated as synonyms.
The first production slice should implement explicit linked-copy routing with
a selected structure, target function mapping, maximum depth, stop condition,
classification gate, loop protection, and delivery-time route snapshot.
Organization changes do not retroactively expose old messages.
Vacancy is a visible delivery/attention state rather than an automatic grant
to an unrelated personal account. Policy may trigger a bounded escalation
after a delay.
## Campaign Distribution
Campaign can use Postbox as an explicit delivery channel through
`postbox.delivery`. A campaign may select Mail, Postbox, both, or a configured
fallback order for a target. It must never switch channels silently.
Validation and build preview stable function/unit/context destinations,
vacancies, hierarchy-copy effects, classifications, and duplicates. Delivery
uses idempotency keys and returns per-target evidence. Postbox owns acceptance,
routing, message state, read/acknowledgement state, and postbox ids; Campaign
owns campaign preparation, jobs, recipient reports, and channel-attempt
evidence.
## Domain Objects ## Domain Objects
The initial domain model should stay small: The initial domain model should stay small:
- `Postbox`: the addressable container. - `PostboxTemplate` and immutable revisions: reusable function/scope
configuration.
- `PostboxAddress`: the stable tenant/function/unit/context destination.
- `Postbox`: the addressable container, materialized when needed.
- `PostboxBinding`: the binding to organization, role, portal, campaign, service, or explicit context. - `PostboxBinding`: the binding to organization, role, portal, campaign, service, or explicit context.
- `PostboxMessage`: a platform-native message or message reference. - `PostboxMessage`: a platform-native message or message reference.
- `PostboxParticipant`: normalized sender, recipient, author, or actor reference. - `PostboxParticipant`: normalized sender, recipient, author, or actor reference.
- `PostboxAttachmentRef`: reference to a file, evidence item, generated campaign artifact, or external attachment. - `PostboxAttachmentRef`: reference to a file, evidence item, generated campaign artifact, or external attachment.
- `PostboxDelivery` and `PostboxRoute`: idempotent producer acceptance and
linked copy/escalation provenance.
- `PostboxGrouping`: a per-user source-preserving inbox projection.
- `PostboxAccessEvent`: auditable record of access-affecting changes and sensitive actions. - `PostboxAccessEvent`: auditable record of access-affecting changes and sensitive actions.
Messages and files should be linked by stable ids and typed references. The postbox module should not import file, mail, or campaign internals. Messages and files should be linked by stable ids and typed references. The postbox module should not import file, mail, or campaign internals.
@@ -130,8 +220,10 @@ Postbox should expose narrow capabilities through core:
Optional consumers: Optional consumers:
- Campaign can use postboxes for campaign sender context, reply intake, review queues, and role-bound access to campaign artifacts. - Campaign can use postboxes for function-targeted delivery, sender context,
- Files can expose file references to a postbox when the actor's role grants access. reply intake, review queues, and access to campaign artifacts.
- Files can expose file references to a postbox when the actor has effective
source-postbox access.
- Portal can show portal-facing postboxes without owning the postbox access model. - Portal can show portal-facing postboxes without owning the postbox access model.
- Mail can bridge external mailbox delivery into postboxes when configured, without making postboxes mailbox-bound. - Mail can bridge external mailbox delivery into postboxes when configured, without making postboxes mailbox-bound.
@@ -173,10 +265,11 @@ The postbox module should emit audit events for:
- retention holds, retention expiry, export, and destruction decisions - retention holds, retention expiry, export, and destruction decisions
Privacy behavior must separate current access from historical evidence. Losing Privacy behavior must separate current access from historical evidence. Losing
a role removes future visibility, but it does not rewrite the fact that a person a function assignment or generic Postbox permission removes future visibility,
previously accessed a message or that a message existed. Deletion and but it does not rewrite the fact that a person previously accessed a message or
destructive retention actions must preserve legally required audit/evidence that a message existed. Deletion and destructive retention actions must
records while removing or redacting content according to the effective policy. preserve legally required audit/evidence records while removing or redacting
content according to the effective policy.
When E2EE is enabled later, retention and audit metadata must remain operable When E2EE is enabled later, retention and audit metadata must remain operable
without decrypting message content. UI copy should be honest: expiry, without decrypting message content. UI copy should be honest: expiry,
@@ -212,9 +305,9 @@ The first implementation should define the backend manifest, permissions, DTOs,
The WebUI should start as an administration and inbox surface: The WebUI should start as an administration and inbox surface:
- postbox directory - postbox directory
- role-bound access explanation - function-bound access explanation
- message list and message detail - message list and message detail
- binding editor for organization and role links - template and binding editor for organization/function links
- audit-visible administrative actions - audit-visible administrative actions
Campaign, files, portal, and mail behavior should arrive as optional integrations after the core postbox model is stable. Campaign, files, portal, and mail behavior should arrive as optional integrations after the core postbox model is stable.