docs: inventory Hub and platform access boundaries for root integration
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e715-b802-70f0-a8fa-590d9ee673a5
This commit is contained in:
parent
e5b67b391d
commit
1182b637c7
5 changed files with 8275 additions and 5 deletions
|
|
@ -17,7 +17,7 @@
|
|||
| workplan | HUB-WP-0007 | finished | — | workplans/HUB-WP-0007-workload-projection-transport.md |
|
||||
| workplan | HUB-WP-0008 | finished | — | workplans/HUB-WP-0008-legacy-message-identity-reconciliation.md |
|
||||
| workplan | HUB-WP-0009 | proposed | — | workplans/HUB-WP-0009-extension-conformance-gaps.md |
|
||||
| workplan | HUB-WP-0012 | proposed | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| workplan | HUB-WP-0012 | active | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| task | HUB-WP-0001-T01 | done | — | workplans/HUB-WP-0001-statehub-bootstrap.md |
|
||||
| task | HUB-WP-0001-T02 | done | — | workplans/HUB-WP-0001-statehub-bootstrap.md |
|
||||
| task | HUB-WP-0001-T03 | done | — | workplans/HUB-WP-0001-statehub-bootstrap.md |
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
| task | HUB-WP-0009-T02 | todo | — | workplans/HUB-WP-0009-extension-conformance-gaps.md |
|
||||
| task | HUB-WP-0009-T03 | todo | — | workplans/HUB-WP-0009-extension-conformance-gaps.md |
|
||||
| task | HUB-WP-0009-T04 | todo | — | workplans/HUB-WP-0009-extension-conformance-gaps.md |
|
||||
| task | HUB-WP-0012-T01 | todo | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| task | HUB-WP-0012-T01 | progress | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| task | HUB-WP-0012-T02 | todo | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| task | HUB-WP-0012-T03 | todo | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
| task | HUB-WP-0012-T04 | todo | — | workplans/HUB-WP-0012-netkingdom-platform-root-access.md |
|
||||
|
|
|
|||
7790
docs/platform-access-inventory.json
Normal file
7790
docs/platform-access-inventory.json
Normal file
File diff suppressed because it is too large
Load diff
339
docs/platform-access-inventory.md
Normal file
339
docs/platform-access-inventory.md
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
# Platform-root access inventory — 2026-09-28
|
||||
|
||||
This is the coverage baseline for HUB-WP-0012-T01, not an access grant or a
|
||||
passing security test. It enumerates 161 Hub source surfaces (88 runtime route
|
||||
registrations, 42 embedded router operations, 31 MCP tools), 39 observed cluster
|
||||
namespaces and nine additional extension/native-management boundaries. All 250
|
||||
observed Deployment/StatefulSet/DaemonSet/CronJob/Service/Ingress objects map to
|
||||
exactly one namespace row. Scaled-down revisions and legacy workloads remain
|
||||
listed so they cannot become unnoticed rollback bypasses.
|
||||
|
||||
The [machine-readable inventory](platform-access-inventory.json) is authoritative
|
||||
for this snapshot. Rows inherit audience, actor/target tenant, action/resource
|
||||
mapping, enforcement point and test owner from their named profile. Platform
|
||||
rows additionally identify responsible repositories and exact Kubernetes objects.
|
||||
Audience candidates and ownership inferred from deployment names are explicitly
|
||||
pending owner confirmation; none establishes an effective platform-root grant.
|
||||
|
||||
## Scope and reproducibility
|
||||
|
||||
Hub source revision is recorded in the JSON. Runtime routes are constructed
|
||||
locally without running startup, sending requests or connecting to a database.
|
||||
The optional inbox router is included separately. Compatibility aliases and
|
||||
FastAPI built-in documentation endpoints are included even when absent from
|
||||
OpenAPI; disabled compatibility groups still belong in the coverage contract.
|
||||
Embedded factories are scanned with their default prefixes and include optional
|
||||
operations: host mounting and feature flags determine effective deployment paths.
|
||||
MCP extraction asserts coverage against CORE_TOOL_NAMES and records its HTTP calls.
|
||||
|
||||
Cluster collection used the explicit railiance01 kubeconfig and metadata-only
|
||||
projection of six resource kinds. No Secret, environment value, mounted file,
|
||||
service-account token or authentication credential was collected. This is not a
|
||||
scan of every CRD, Pod/Job, host process, external provider or tenant application
|
||||
endpoint. Native execution and external-control rows keep those inventory gaps
|
||||
visible. Root administration of tenant infrastructure is distinct from an
|
||||
unreviewed grant to its business data.
|
||||
|
||||
Run from hub-core:
|
||||
|
||||
```sh
|
||||
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python tools/build_access_inventory.py \
|
||||
--inventory docs/platform-access-inventory.json --check
|
||||
```
|
||||
|
||||
Omit `--check` to refresh source rows after intentional changes, then review the
|
||||
diff. Cluster metadata is a dated reviewed input, not silently refreshed by this
|
||||
command. The checker detects source drift, missing profile/test references and
|
||||
missing/duplicate cluster-object mappings. It does not test authorization. Actual
|
||||
allow/deny cases are all marked `not-run`; implementation tasks must supply the
|
||||
client fixtures, isolated mutations, independent readbacks and live receipts.
|
||||
|
||||
## Findings that affect implementation
|
||||
|
||||
1. **Documentation routes have overlapping handlers.** GET `/docs` and `/openapi.json`
|
||||
each register both FastAPI's built-in handler and a compatibility alias.
|
||||
Protecting only the compatibility handler leaves another dispatch path.
|
||||
T04 must test effective routing, including HEAD and slash normalization.
|
||||
2. **MCP is not synonymous with the standalone runtime.** Many tools call
|
||||
`/messages`, `/domains`, `/state/summary` and other host routes rather than
|
||||
`/ports/...`. T04/T05 need an explicit backend/migration mapping and per-caller
|
||||
authentication. A successful native-port test does not cover these tools.
|
||||
3. **Embedded APIs are independent entry points.** The host owns authentication,
|
||||
policy injection and any prefix overrides; an Ingress change cannot protect
|
||||
a host that mounts the SDK elsewhere. Inventory actual consumer mounts before
|
||||
freezing T01, using the retirement route/caller ledgers.
|
||||
4. **39 namespaces do not mean 39 login surfaces.** Controllers, backing stores,
|
||||
scaled-down revisions and the notice page should be managed through their
|
||||
owner/Kubernetes path, not exposed as new human-facing services. Each owner
|
||||
must split management and application audiences within its namespace row.
|
||||
5. **Extensions/native administration still need owner evidence.** Ops Hub's
|
||||
manifest names `service.ops-hub.http`, a framework API, console and CLI;
|
||||
standalone live resolution is unproven. Fabric hosting is independently
|
||||
blocked under RAIL-FAB-WP-0028. SSH, Kubernetes, GitOps, host jobs and provider
|
||||
control planes need their own root entitlement receipts.
|
||||
|
||||
## Proposed acceptance cases
|
||||
|
||||
Every non-health surface runs ROOT, OTHER, INVALID, REVOKE, OUTAGE, BYPASS and
|
||||
CALLER from the JSON; native privileged actions additionally run APPROVAL.
|
||||
`/healthz` gets HEALTH instead of pretending anonymous probes should be denied.
|
||||
These are test specifications, not completed tests:
|
||||
|
||||
| Case | Required observation |
|
||||
| --- | --- |
|
||||
| ROOT | Verified immutable root identity + current entitlement + AAL2 succeeds; independent readback and actor audit |
|
||||
| OTHER | Ordinary user and tenant administrator cannot perform platform operations or learn unauthorized tenant data |
|
||||
| INVALID | Anonymous, forged username/header, wrong audience/issuer and expired token rejected without side effect |
|
||||
| REVOKE | Grant removal, account suspension and logout deny within the specified bound; current authority rechecked for privileged mutation |
|
||||
| OUTAGE | Untrusted/unavailable policy or required audit cannot authorize mutation |
|
||||
| BYPASS | Direct Service, aliases, MCP and embedded hosts enforce the same decision |
|
||||
| CALLER | Named workload receives only its grant; spoofed sender, delegation and inherited root authority fail |
|
||||
| APPROVAL | Full root entitlement does not skip action-specific confirmation/approval; use reversible or isolated targets |
|
||||
| HEALTH | Anonymous liveness reveals no subject, tenant, dependency or business details |
|
||||
|
||||
## Hub surface register
|
||||
|
||||
The table lists every discovered source operation. JSON retains factory/handler,
|
||||
source location, current gate observation and MCP target call expressions.
|
||||
Duplicate runtime method/path rows are intentional separate registrations.
|
||||
|
||||
| Kind/profile | Operation | Source/handler |
|
||||
| --- | --- | --- |
|
||||
| runtime-http / hub-api | `GET /annotation-categories` | `annotation_categories` |
|
||||
| runtime-http / hub-api | `GET /annotations` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api-consumers` | `list_consumers` |
|
||||
| runtime-http / hub-api | `GET /api/v2/annotation-categories` | `annotation_categories` |
|
||||
| runtime-http / hub-api | `GET /api/v2/annotations` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api/v2/api-consumers` | `list_consumers` |
|
||||
| runtime-http / hub-api | `GET /api/v2/decision-records` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api/v2/deployment-records` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api/v2/docs` | `docs` |
|
||||
| runtime-http / hub-api | `GET /api/v2/event-types` | `event_types` |
|
||||
| runtime-http / hub-api | `GET /api/v2/hub-capability-manifests` | `list_manifests` |
|
||||
| runtime-http / hub-api | `GET /api/v2/hub-registry` | `hub_registry` |
|
||||
| runtime-http / hub-api | `GET /api/v2/hubs` | `list_hubs` |
|
||||
| runtime-http / hub-api | `GET /api/v2/interaction-events` | `list_interactions` |
|
||||
| runtime-http / hub-api | `GET /api/v2/openapi.json` | `openapi_json` |
|
||||
| runtime-http / hub-api | `GET /api/v2/openapi.yaml` | `openapi_yaml` |
|
||||
| runtime-http / hub-api | `GET /api/v2/outcome-signals` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api/v2/policy-scopes` | `policy_scopes` |
|
||||
| runtime-http / hub-api | `GET /api/v2/requirement-candidates` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /api/v2/widget-types` | `widget_types` |
|
||||
| runtime-http / hub-api | `GET /api/v2/widgets` | `list_widgets` |
|
||||
| runtime-http / hub-api | `GET /console` | `console` |
|
||||
| runtime-http / hub-api | `GET /decision-records` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /deployment-records` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /docs/oauth2-redirect` | `swagger_ui_redirect` |
|
||||
| runtime-http / hub-api | `GET /docs` | `swagger_ui_html` |
|
||||
| runtime-http / hub-api | `GET /docs` | `docs` |
|
||||
| runtime-http / hub-api | `GET /event-types` | `event_types` |
|
||||
| runtime-http / minimal-health | `GET /healthz` | `healthz` |
|
||||
| runtime-http / hub-api | `GET /hub-capability-manifests` | `list_manifests` |
|
||||
| runtime-http / hub-api | `GET /hub-registry` | `hub_registry` |
|
||||
| runtime-http / hub-api | `GET /hubs` | `list_hubs` |
|
||||
| runtime-http / hub-api | `GET /interaction-events` | `list_interactions` |
|
||||
| runtime-http / hub-api | `GET /openapi.json` | `openapi` |
|
||||
| runtime-http / hub-api | `GET /openapi.json` | `openapi_json` |
|
||||
| runtime-http / hub-api | `GET /openapi.yaml` | `openapi_yaml` |
|
||||
| runtime-http / hub-api | `GET /outcome-signals` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /policy-scopes` | `policy_scopes` |
|
||||
| runtime-http / hub-api | `GET /ports/messaging/messages` | `list_messages` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/repository-navigation/facets/{facet_kind}/{facet_value}` | `query_facet` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/repository-navigation/repositories` | `query_repositories` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/statehub-inbox` | `inbox` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/workloads/resolve` | `resolve_workload` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/workloads` | `query_workloads` |
|
||||
| runtime-http / hub-api | `GET /ports/projections/{projection_id}` | `query_projection` |
|
||||
| runtime-http / hub-api | `GET /ports/registry/registrations/{hub_slug}/audit` | `registration_audit` |
|
||||
| runtime-http / hub-api | `GET /ports/registry/registrations/{hub_slug}` | `resolve_registration` |
|
||||
| runtime-http / hub-api | `GET /readyz` | `readyz` |
|
||||
| runtime-http / hub-api | `GET /redoc` | `redoc_html` |
|
||||
| runtime-http / hub-api | `GET /requirement-candidates` | `empty_collection` |
|
||||
| runtime-http / hub-api | `GET /widget-types` | `widget_types` |
|
||||
| runtime-http / hub-api | `GET /widgets` | `list_widgets` |
|
||||
| runtime-http / hub-api | `HEAD /docs/oauth2-redirect` | `swagger_ui_redirect` |
|
||||
| runtime-http / hub-api | `HEAD /docs` | `swagger_ui_html` |
|
||||
| runtime-http / hub-api | `HEAD /openapi.json` | `openapi` |
|
||||
| runtime-http / hub-api | `HEAD /redoc` | `redoc_html` |
|
||||
| runtime-http / hub-api | `PATCH /api/v2/hub-capability-manifests/{manifest_id}` | `patch_manifest` |
|
||||
| runtime-http / hub-api | `PATCH /hub-capability-manifests/{manifest_id}` | `patch_manifest` |
|
||||
| runtime-http / hub-api | `POST /annotations` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api-consumers/{consumer_id}/api-keys` | `create_key` |
|
||||
| runtime-http / hub-api | `POST /api-consumers` | `create_consumer` |
|
||||
| runtime-http / hub-api | `POST /api/v2/annotations` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api/v2/api-consumers/{consumer_id}/api-keys` | `create_key` |
|
||||
| runtime-http / hub-api | `POST /api/v2/api-consumers` | `create_consumer` |
|
||||
| runtime-http / hub-api | `POST /api/v2/decision-records` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api/v2/deployment-records` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api/v2/hub-capability-manifests/{manifest_id}/activate` | `activate_manifest` |
|
||||
| runtime-http / hub-api | `POST /api/v2/hub-capability-manifests` | `create_manifest` |
|
||||
| runtime-http / hub-api | `POST /api/v2/hubs` | `create_hub` |
|
||||
| runtime-http / hub-api | `POST /api/v2/interaction-events` | `create_interaction` |
|
||||
| runtime-http / hub-api | `POST /api/v2/outcome-signals` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api/v2/requirement-candidates` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /api/v2/token` | `token` |
|
||||
| runtime-http / hub-api | `POST /api/v2/widgets` | `create_widget` |
|
||||
| runtime-http / hub-api | `POST /decision-records` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /deployment-records` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /hub-capability-manifests/{manifest_id}/activate` | `activate_manifest` |
|
||||
| runtime-http / hub-api | `POST /hub-capability-manifests` | `create_manifest` |
|
||||
| runtime-http / hub-api | `POST /hubs` | `create_hub` |
|
||||
| runtime-http / hub-api | `POST /interaction-events` | `create_interaction` |
|
||||
| runtime-http / hub-api | `POST /outcome-signals` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /ports/events/interaction` | `append_interaction` |
|
||||
| runtime-http / hub-api | `POST /ports/events/progress` | `append_progress` |
|
||||
| runtime-http / hub-api | `POST /ports/messaging/messages` | `send_message` |
|
||||
| runtime-http / hub-api | `POST /ports/registry/registrations` | `register_extension` |
|
||||
| runtime-http / hub-api | `POST /requirement-candidates` | `accept_deferred` |
|
||||
| runtime-http / hub-api | `POST /token` | `token` |
|
||||
| runtime-http / hub-api | `POST /widgets` | `create_widget` |
|
||||
| mcp / mcp-client | `accept_capability_request` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `append_progress` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `check_repo_doi` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_alerts` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_capability_request` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_doi_summary` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_domain` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_domain_summary` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_gdpr_report` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_messages` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_repository_navigation_facet` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_risks` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `get_state_summary` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `ingest_tpsc_tool` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `list_capabilities` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `list_capability_requests` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `list_domain_repos` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `list_domains` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `list_services` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `mark_message_read` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `query_repository_navigation` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `query_workloads` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `register_capability` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `register_repo` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `register_service` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `reply_to_message` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `request_capability` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `resolve_workload_reference` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `send_message` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `update_capability_request_status` | `hub_core/mcp/server.py` |
|
||||
| mcp / mcp-client | `update_repo_path` | `hub_core/mcp/server.py` |
|
||||
| embedded-http / embedded-host | `GET /capability-catalog/` | `create_capability_catalog_router` |
|
||||
| embedded-http / embedded-host | `PATCH /capability-catalog/{entry_id}` | `create_capability_catalog_router` |
|
||||
| embedded-http / embedded-host | `POST /capability-catalog/` | `create_capability_catalog_router` |
|
||||
| embedded-http / embedded-host | `GET /capability-requests/` | `create_capability_request_read_router` |
|
||||
| embedded-http / embedded-host | `GET /capability-requests/{request_id}` | `create_capability_request_read_router` |
|
||||
| embedded-http / embedded-host | `PATCH /capability-requests/{request_id}` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `PATCH /capability-requests/{request_id}/status` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `POST /capability-requests/` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `POST /capability-requests/{request_id}/accept` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `POST /capability-requests/{request_id}/dispute` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `POST /capability-requests/{request_id}/reroute` | `create_capability_request_write_router` |
|
||||
| embedded-http / embedded-host | `GET /domains/` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `GET /domains/{slug}` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `PATCH /domains/{slug}` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `PATCH /domains/{slug}/archive` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `PATCH /domains/{slug}/rename` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `POST /domains/` | `create_domains_router` |
|
||||
| embedded-http / embedded-host | `GET /messages/` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `GET /messages/thread/{thread_id}` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `PATCH /messages/{message_id}/archive` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `PATCH /messages/{message_id}/read` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `POST /messages/` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `POST /messages/{message_id}/reply` | `create_messages_router` |
|
||||
| embedded-http / embedded-host | `GET /policy/{name}` | `create_policy_router` |
|
||||
| embedded-http / embedded-host | `PUT /policy/{name}` | `create_policy_router` |
|
||||
| embedded-http / embedded-host | `GET /progress/` | `create_progress_router` |
|
||||
| embedded-http / embedded-host | `GET /progress/alerts` | `create_progress_router` |
|
||||
| embedded-http / embedded-host | `GET /progress/risks` | `create_progress_router` |
|
||||
| embedded-http / embedded-host | `POST /progress/` | `create_progress_router` |
|
||||
| embedded-http / embedded-host | `GET /repos/` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `GET /repos/by-fingerprint` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `GET /repos/by-remote` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `GET /repos/{slug}` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `PATCH /repos/{slug}` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `POST /repos/` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `POST /repos/{slug}/paths` | `create_repos_router` |
|
||||
| embedded-http / embedded-host | `GET /tpsc/catalog/` | `create_tpsc_router` |
|
||||
| embedded-http / embedded-host | `GET /tpsc/catalog/{slug}` | `create_tpsc_router` |
|
||||
| embedded-http / embedded-host | `GET /tpsc/report/gdpr` | `create_tpsc_router` |
|
||||
| embedded-http / embedded-host | `GET /tpsc/snapshots/` | `create_tpsc_router` |
|
||||
| embedded-http / embedded-host | `POST /tpsc/catalog/` | `create_tpsc_router` |
|
||||
| embedded-http / embedded-host | `POST /tpsc/ingest/` | `create_tpsc_router` |
|
||||
|
||||
## Platform and extension register
|
||||
|
||||
All rows require owner review and root acceptance. The JSON contains exact
|
||||
object names, hosts, desired/ready replicas and service types for cluster rows.
|
||||
This mapping identifies accountable review destinations, not completed review.
|
||||
|
||||
| Boundary | Owner / test owner | Coverage |
|
||||
| --- | --- | --- |
|
||||
| `namespace:activity-core` | activity-core | 22 observed objects |
|
||||
| `namespace:approval-engine` | approval-engine | 2 observed objects |
|
||||
| `namespace:argocd` | railiance-platform / railiance-enablement | 8 observed objects |
|
||||
| `namespace:audit-core` | audit-core | 3 observed objects |
|
||||
| `namespace:bao-notice` | railiance-platform | 4 observed objects |
|
||||
| `namespace:canned-prompts` | rapp-canned-prompts | 2 observed objects |
|
||||
| `namespace:cert-manager` | railiance-platform | 6 observed objects |
|
||||
| `namespace:cnpg-system` | rapp-postgres | 2 observed objects |
|
||||
| `namespace:core-hub` | hub-core / rapp-core-hub / repo-manager | 6 observed objects |
|
||||
| `namespace:coulomb` | railiance-platform (probe owner to confirm) | 3 observed objects |
|
||||
| `namespace:coulomb-social` | coulomb-social | 3 observed objects |
|
||||
| `namespace:databases` | rapp-postgres / railiance-platform | 18 observed objects |
|
||||
| `namespace:default` | railiance-platform | 1 observed objects |
|
||||
| `namespace:email-connect` | email-connect | 2 observed objects |
|
||||
| `namespace:external-secrets` | railiance-platform | 5 observed objects |
|
||||
| `namespace:flex-auth` | flex-auth | 12 observed objects |
|
||||
| `namespace:forgejo` | railiance-forge / railiance-platform | 6 observed objects |
|
||||
| `namespace:informed-decision` | informed-decision | 4 observed objects |
|
||||
| `namespace:inter-hub` | prj-state-hub-retirement / railiance-platform | 2 observed objects |
|
||||
| `namespace:issue-core` | issue-core | 2 observed objects |
|
||||
| `namespace:knative-serving` | rail-knative / railiance-platform | 11 observed objects |
|
||||
| `namespace:kourier-system` | rail-knative / railiance-platform | 3 observed objects |
|
||||
| `namespace:kube-system` | rail-kubernetes / railiance-platform | 10 observed objects |
|
||||
| `namespace:mfa` | net-kingdom / key-cape | 7 observed objects |
|
||||
| `namespace:openbao` | rapp-openbao / railiance-platform | 8 observed objects |
|
||||
| `namespace:platform-pg-drill` | rapp-postgres | 2 observed objects |
|
||||
| `namespace:policy-nexus` | policy-nexus | 4 observed objects |
|
||||
| `namespace:rapp-qonto` | rapp-qonto | 28 observed objects |
|
||||
| `namespace:rapp-qonto-egress` | rapp-qonto | 2 observed objects |
|
||||
| `namespace:rein-aharness` | rein-aharness | 1 observed objects |
|
||||
| `namespace:reuse` | reuse-surface | 7 observed objects |
|
||||
| `namespace:sbom-nexus` | sbom-nexus | 2 observed objects |
|
||||
| `namespace:sso` | net-kingdom / key-cape | 15 observed objects |
|
||||
| `namespace:state-hub` | state-hub | 4 observed objects |
|
||||
| `namespace:target-revenue` | target-revenue | 6 observed objects |
|
||||
| `namespace:telemetry` | rapp-telemetry / railiance-platform | 12 observed objects |
|
||||
| `namespace:tenant-engine` | tenant-engine | 2 observed objects |
|
||||
| `namespace:user-engine` | user-engine | 9 observed objects |
|
||||
| `namespace:vergabe-demo-company` | vergabe-demo-company | 4 observed objects |
|
||||
| `management:ops-hub` | ops-hub | service.ops-hub.http, framework /api/v2, ops-console, ops-bootstrap |
|
||||
| `management:financial-fabric` | fin-hub / railiance-fabric | financial graph owner API and projection/export |
|
||||
| `management:repo-manager` | repo-manager | registry/work projections, governed CLI/Git mutations, Forgejo-backed publisher |
|
||||
| `management:kubernetes` | rail-kubernetes / railiance-platform | realm:kubernetes/railiance01; API, RBAC, nodes, workload lifecycle |
|
||||
| `management:ssh-tunnels` | ops-warden / ops-bridge | railiance01 SSH certificate/principal and named tunnels |
|
||||
| `management:gitops-deploy` | railiance-platform / railiance-enablement | ArgoCD Core CLI, repo authorization, per-workload release/rollback |
|
||||
| `management:secrets-engine` | secrets-engine / railiance-platform | credential issue/rotate/revoke and approval-bound OpenBao operations |
|
||||
| `management:host-jobs` | railiance-platform / activity-core | host systemd timers, cron, backup/restore and DR execution |
|
||||
| `management:external-control` | railiance-platform / net-kingdom | DNS, registrar, hosting, object storage and off-cluster recovery administration |
|
||||
|
||||
## Remaining T01 decisions
|
||||
|
||||
- NetKingdom/User Engine: verify root `(iss, sub)` and the entitlement mapping;
|
||||
confirm registered audiences, MFA journey and measurable revocation bounds.
|
||||
- flex-auth/Tenant Engine: ratify action/resource names, authoritative fact
|
||||
checks, cross-tenant root administration and decision/audit obligations.
|
||||
- Hub/extension owners: map effective embedded mounts, legacy MCP destinations,
|
||||
active extension discovery and public/health exceptions; resolve duplicate docs.
|
||||
- Railiance owners: confirm namespace ownership, enumerate per-service audiences
|
||||
and endpoint catalogs, CRD/Job/host/provider management paths, and the native
|
||||
SSH/Kubernetes/GitOps grants. Unknown surfaces cannot be marked passed.
|
||||
- All reviewers: approve the versioned profile and supply executable enforcement
|
||||
fixtures/receipts. Until then T01 remains in progress and public exposure stays
|
||||
gated. No new workplan or live configuration change was made by this inventory.
|
||||
|
||||
Related evidence: [access blueprint](netkingdom-access-blueprint.md),
|
||||
[HUB-WP-0012](../workplans/HUB-WP-0012-netkingdom-platform-root-access.md),
|
||||
`ops-hub/registry/hub-extension/v0.1.0/ops-hub.extension.json`, retirement project
|
||||
`inventory/routes.yaml` and `inventory/jobs-callers-ops.yaml`.
|
||||
131
tools/build_access_inventory.py
Normal file
131
tools/build_access_inventory.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Inventory source surfaces without network calls, database access or credentials.
|
||||
|
||||
Run with hub-core's runtime environment. --check detects source-surface drift;
|
||||
it is not an authorization conformance test. Platform snapshot is reviewed input.
|
||||
"""
|
||||
import argparse
|
||||
import ast
|
||||
import inspect
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def discover(root):
|
||||
sys.path.insert(0, str(root))
|
||||
from hub_core.runtime.app import create_app
|
||||
from hub_core.runtime.config import RuntimeSettings
|
||||
from hub_core.runtime.inbox_projection import create_inbox_projection_router
|
||||
|
||||
rows = []
|
||||
|
||||
def routes(router):
|
||||
for route in router.routes:
|
||||
if hasattr(route, 'original_router'):
|
||||
yield from routes(route.original_router)
|
||||
elif hasattr(route, 'methods'):
|
||||
yield route
|
||||
else:
|
||||
raise ValueError(f'Uninventoried route type: {type(route).__name__}')
|
||||
|
||||
# Construction only: no lifespan/startup and no requests or DB connection.
|
||||
app = create_app(settings=RuntimeSettings())
|
||||
for route in [*routes(app), *routes(create_inbox_projection_router())]:
|
||||
endpoint = route.endpoint
|
||||
source = inspect.getsource(endpoint)
|
||||
module = endpoint.__module__
|
||||
gate = ('shared-bearer' if '_protected(' in source or module.endswith('inbox_projection')
|
||||
else 'no-identity-check-in-handler')
|
||||
for method in sorted(route.methods):
|
||||
rows.append(dict(id=f'http:{method}:{route.path}:{module}.{endpoint.__name__}', kind='runtime-http',
|
||||
method=method, path=route.path, profile=('minimal-health' if route.path == '/healthz' else 'hub-api'),
|
||||
current_gate=gate, source=module,
|
||||
conditional=module.endswith('inbox_projection'),
|
||||
handler=endpoint.__name__))
|
||||
|
||||
verbs = {'get', 'post', 'patch', 'put', 'delete', 'head', 'options'}
|
||||
for path in sorted((root / 'hub_core/routers').glob('*.py')):
|
||||
tree = ast.parse(path.read_text())
|
||||
for factory in tree.body:
|
||||
if not isinstance(factory, ast.FunctionDef) or not factory.name.startswith('create_'):
|
||||
continue
|
||||
prefix = ''
|
||||
for node in ast.walk(factory):
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'APIRouter':
|
||||
for kw in node.keywords:
|
||||
if kw.arg == 'prefix':
|
||||
if isinstance(kw.value, ast.Constant):
|
||||
prefix = kw.value.value
|
||||
elif isinstance(kw.value, ast.Name):
|
||||
defaults = dict(zip([a.arg for a in factory.args.kwonlyargs], factory.args.kw_defaults))
|
||||
prefix = ast.literal_eval(defaults[kw.value.id])
|
||||
else:
|
||||
raise ValueError('Unresolved SDK prefix')
|
||||
for node in ast.walk(factory):
|
||||
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
continue
|
||||
for dec in node.decorator_list:
|
||||
if isinstance(dec, ast.Call) and isinstance(dec.func, ast.Attribute) and dec.func.attr in verbs:
|
||||
route_path = prefix + ast.literal_eval(dec.args[0])
|
||||
method = dec.func.attr.upper()
|
||||
rows.append(dict(id=f'sdk:{factory.name}:{method}:{route_path}',
|
||||
kind='embedded-http', method=method, path=route_path,
|
||||
profile='embedded-host', factory=factory.name,
|
||||
source=str(path.relative_to(root)), line=node.lineno,
|
||||
current_gate='host-injected; not established by inventory',
|
||||
conditional=True))
|
||||
|
||||
path = root / 'hub_core/mcp/server.py'
|
||||
tree = ast.parse(path.read_text())
|
||||
expected = None
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.Assign) and any(isinstance(t, ast.Name) and t.id == 'CORE_TOOL_NAMES' for t in node.targets):
|
||||
expected = set(ast.literal_eval(node.value.args[0]))
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
continue
|
||||
for dec in node.decorator_list:
|
||||
if isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and dec.func.id == 'register':
|
||||
name = ast.literal_eval(dec.args[0])
|
||||
calls = []
|
||||
for call in ast.walk(node):
|
||||
if isinstance(call, ast.Call) and isinstance(call.func, ast.Attribute) and call.func.attr in {'_get','_post','_patch','_put','_delete'}:
|
||||
calls.append(dict(method=call.func.attr[1:].upper(), path_expression=ast.unparse(call.args[0])))
|
||||
rows.append(dict(id=f'mcp:{name}', kind='mcp', tool=name, profile='mcp-client',
|
||||
source=str(path.relative_to(root)), line=node.lineno,
|
||||
target_calls=calls, current_gate='no per-user credential forwarding in base wrapper'))
|
||||
assert expected == {r['tool'] for r in rows if r['kind'] == 'mcp'}
|
||||
assert len(rows) == len({r['id'] for r in rows}), 'Duplicate surface identity'
|
||||
return sorted(rows, key=lambda r:r['id'])
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--root', type=Path, default=Path(__file__).resolve().parents[1])
|
||||
parser.add_argument('--inventory', type=Path, required=True)
|
||||
parser.add_argument('--check', action='store_true')
|
||||
args = parser.parse_args()
|
||||
data = json.loads(args.inventory.read_text())
|
||||
found = discover(args.root)
|
||||
if args.check:
|
||||
assert data['hub_surfaces'] == found, 'Source inventory drift; regenerate and review'
|
||||
else:
|
||||
data['hub_surfaces'] = found
|
||||
args.inventory.write_text(json.dumps(data, indent=2)+'\n')
|
||||
cases = data['acceptance_cases']
|
||||
profiles = data['profiles']
|
||||
for row in data['hub_surfaces'] + data['platform_surfaces']:
|
||||
profile = profiles[row['profile']]
|
||||
assert all(profile.get(k) for k in ('audience','actor_tenant','target_tenant','action_resource_rule','enforcement','test_owner','cases'))
|
||||
assert all(c in cases for c in profile['cases'])
|
||||
objects = data['cluster_snapshot']
|
||||
mapped = [x for r in data['platform_surfaces'] for x in r.get('objects',[])]
|
||||
keys = lambda xs: sorted((x['kind'],x['namespace'],x['name']) for x in xs)
|
||||
assert keys(mapped) == keys(objects), 'Cluster object coverage mismatch or duplicates'
|
||||
assert all(r.get('owner') and r.get('review_status') for r in data['platform_surfaces'])
|
||||
print(f"{len(found)} Hub surfaces; {len(data['platform_surfaces'])} platform rows; {len(objects)} cluster objects; inventory checks pass")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "NetKingdom identity and tenant integration: platform-root first"
|
||||
domain: infotech
|
||||
repo: hub-core
|
||||
status: proposed
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: infotech
|
||||
|
|
@ -43,7 +43,7 @@ existing retirement and rollout plans retain their tasks. Core Hub receives no
|
|||
new product feature work. This planning session does not implement or activate
|
||||
grants, enroll factors, deploy policies, expose services or retire State Hub.
|
||||
|
||||
Status is proposed because cross-owner policy, root identity binding and live
|
||||
Inventory work is active. Cross-owner policy, root identity binding and live
|
||||
acceptance are not yet reviewed. The user has selected the root-first scope;
|
||||
there is no need to reopen that product decision. Dependencies below are
|
||||
per-task sequencing, not a blanket wait for every related workplan to finish.
|
||||
|
|
@ -52,7 +52,7 @@ per-task sequencing, not a blanket wait for every related workplan to finish.
|
|||
|
||||
```task
|
||||
id: HUB-WP-0012-T01
|
||||
status: todo
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "204f4fb0-e240-5558-8790-5985517b85e0"
|
||||
```
|
||||
|
|
@ -70,6 +70,16 @@ Done when no published route/tool or active platform surface lacks a row and
|
|||
owner, contract reviewers' decisions are recorded, and M1 success/deny cases
|
||||
are executable specifications. Unknown/disputed rows remain visible blockers.
|
||||
|
||||
2026-09-28: the [access inventory](../docs/platform-access-inventory.md) now
|
||||
enumerates 161 Hub source surfaces and 48 platform/extension boundaries, covering
|
||||
250 observed cluster objects. Its machine-readable profiles specify owners,
|
||||
audiences, actor/target tenants, enforcement and acceptance cases; the checker
|
||||
detects source drift and incomplete object mappings. Duplicate docs handlers,
|
||||
legacy MCP targets and unresolved native/extension paths are explicit findings.
|
||||
Owner review, effective host/per-service route expansion, policy vocabulary and
|
||||
authenticated acceptance remain open, so T01 is `progress`, not `done`. No
|
||||
platform-root login or enforcement test is claimed by inventory validation.
|
||||
|
||||
## T02 — Bind platform-root identity, login, tenant and revocation
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue