152 lines
7.2 KiB
Markdown
152 lines
7.2 KiB
Markdown
# Role-Based Service Directory Concept
|
|
|
|
GovOPlaN Portal should expose a role-aware service directory. The directory is
|
|
not a marketing catalogue; it is the user-facing map of what a person can do in
|
|
the configured institution.
|
|
|
|
Portal owns presentation, discovery, entry, and channel-specific availability.
|
|
It must not become the long-term owner of the institutional service definition.
|
|
A shared Services contract should own the versioned promise independently so
|
|
Cases, Forms, Workflow Engine, Reporting, external publication, and more than
|
|
one portal surface can consume the same definition.
|
|
|
|
## Purpose
|
|
|
|
The service directory should help users find the right administrative action
|
|
without knowing the internal module layout. Available services depend on
|
|
tenant, role, organization unit, policies, installed modules, and configuration
|
|
packages.
|
|
|
|
Examples:
|
|
|
|
- apply for a permit
|
|
- submit documents for an existing case
|
|
- request an appointment
|
|
- send a secure postbox message
|
|
- report an issue
|
|
- book a resource
|
|
- start an internal workflow
|
|
|
|
## Contract
|
|
|
|
A service entry should describe:
|
|
|
|
- service key and label
|
|
- owning module or configuration package
|
|
- audience and role/function requirements
|
|
- required installed/enabled modules
|
|
- required capabilities
|
|
- form, workflow, postbox, task, or external connector entry point
|
|
- policy and availability blockers
|
|
- user-facing explanation and required documents
|
|
- audit and evidence expectations
|
|
- version and effective interval
|
|
- legal or organizational basis, deadlines, fees, remedies, and service-level
|
|
expectations
|
|
- responsible organization/function, mandate/jurisdiction, result/decision,
|
|
publication, and records bindings
|
|
|
|
The portal should request service contributions through core-mediated
|
|
capability and UI contribution contracts. It must not import domain module
|
|
internals.
|
|
|
|
The first implementation may consume module-contributed service definitions.
|
|
Create a separate `govoplan-services` repository only when definitions require
|
|
independent persistence/lifecycle and are consumed outside Portal by at least
|
|
one other procedure or publication surface. Extraction must keep stable service
|
|
keys and compatibility for existing Portal links.
|
|
|
|
Service visibility must consume access semantics through kernel capabilities:
|
|
|
|
- `access.semanticDirectory` resolves the actor's identity, account,
|
|
organization-unit function assignments, delegations, and role mappings.
|
|
- `access.explanation` provides the explanation shown when a service is
|
|
available or blocked because of a missing function, role, right, or policy.
|
|
|
|
Service entries should support function requirements directly, not only role
|
|
requirements. A function requirement may apply to one organization unit or to
|
|
that unit and all subunits, matching the access assignment scope.
|
|
|
|
## UX Rule
|
|
|
|
The directory should explain unavailable services when the reason is useful:
|
|
missing role, disabled module, tenant policy, missing connector, maintenance
|
|
mode, or unavailable external provider. It should hide only services that are
|
|
irrelevant or intentionally undiscoverable by policy.
|
|
|
|
## Implemented Slice
|
|
|
|
`portal.service_directory` consumes the optional `services.definitions`
|
|
capability. It returns only tenant-scoped definitions and distinguishes
|
|
`available`, `unavailable`, and `hidden` states. Draft, retired,
|
|
outside-effective-time, and audience-inapplicable services are hidden. A
|
|
suspended service or missing required module/capability remains discoverable
|
|
with stable reason codes and its configured explanation reference.
|
|
|
|
The definition provider remains responsible for access and policy filtering.
|
|
Portal cannot widen its result and returns an empty directory when no provider
|
|
is installed. No service definition is persisted in Portal.
|
|
|
|
Tenant/package specializations are derived before presentation through Core's
|
|
restrictive Service rule. They carry their parent version and may narrow
|
|
audience, channels, effective time, and publication or add prerequisites,
|
|
required evidence, legal bases, and bindings. They cannot silently loosen the
|
|
system/package definition.
|
|
|
|
Policy, Mandate, connector, maintenance, and configuration requirements are
|
|
evaluated through the optional `services.availability` capability. Module and
|
|
capability requirements are checked against the active registry, and audience
|
|
requirements against the supplied actor projection. Unknown requirements fail
|
|
closed. Each requirement declares whether failure remains visible with an
|
|
explanation or makes the entry undiscoverable.
|
|
|
|
`GET /api/v1/portal/services` and the `/portal` WebUI expose this projection.
|
|
The API derives audience tokens from the authenticated principal instead of
|
|
accepting caller-provided audience claims. Generic `public` and `authenticated`
|
|
tokens are combined with stable account, identity, group, role, active function,
|
|
and organization-unit tokens. Function slugs are included only after the
|
|
optional Access semantic directory confirms an active, effective, same-tenant
|
|
assignment. Missing or failing directory providers narrow the result.
|
|
|
|
The WebUI provides bounded search, availability filtering, explanations, and
|
|
one launch action. `POST /api/v1/portal/services/{service_id}/launch` resolves
|
|
the entry again at launch time so a stale browser projection cannot bypass a
|
|
new audience, publication, effective-time, module, or capability restriction.
|
|
Direct URL entries return only validated local or credential-free HTTP(S)
|
|
redirects.
|
|
|
|
Case, form, and workflow bindings delegate to owner capabilities. Their launch result
|
|
must retain the exact Service reference, tenant, binding kind, target reference,
|
|
and replay-safe idempotency key. Portal never writes their tables. Missing
|
|
launchers produce `service.launcher.missing:<kind>` and make the entry visibly
|
|
unavailable. Forms Runtime now provides a definition-aware launcher when Forms
|
|
and Forms Runtime are active. It resolves an exact published
|
|
`<form-id>/<revision>`, validates launch values, and retains Service/binding
|
|
provenance. Reduced installations still fail closed rather than simulating a
|
|
submission in Portal.
|
|
|
|
## Applicant Status Presentation
|
|
|
|
Portal also presents Forms Runtime's bounded applicant-status projection at
|
|
`/portal/status/:trackingId`. It does not persist a status, inspect a Form
|
|
submission, or decide the disclosure policy. Forms Runtime resolves the tenant
|
|
through the Core `application_status.projection` contract and remains
|
|
authoritative for all access decisions.
|
|
|
|
The page adapts to the configured grant:
|
|
|
|
- authenticated-only access offers sign-in and then uses the applicant-bound
|
|
status endpoint;
|
|
- email-link access accepts the linked email address and always reports the
|
|
same request outcome, whether or not it matched; a delivered link carries a
|
|
short-lived secret that can be resent and replaces its predecessor; and
|
|
- permanent-link access loads from the high-entropy tracking URL without
|
|
authentication.
|
|
|
|
All modes render only title, current lifecycle state, update time, receipt
|
|
identifier, and the bounded public timeline supplied by Forms Runtime. Portal
|
|
must not infer missing milestones or expose values, people, evidence, internal
|
|
notes, or handoff details. A reload action re-fetches the authoritative
|
|
projection. Missing, disabled, revoked, expired, or unauthorized grants share
|
|
a non-enumerating unavailable state.
|