mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-14 08:10:26 +00:00
Preserve resource URLs across runtime identity changes
Contract-Neutral: Docker URL migration touches shared monitor ingest files without changing the host deletion or re-enrollment contract.
This commit is contained in:
@@ -4990,3 +4990,10 @@ restart. `internal/api/host_agent_removal_lifecycle_integration_test.go`,
|
||||
`internal/monitoring/monitor_host_agent_removal_lifecycle_test.go`, and the
|
||||
existing `scripts/installtests/agent_state_dir_lifecycle_test.go`,
|
||||
`install_sh_test.go`, and `install_ps1_test.go` are the production-path proofs.
|
||||
|
||||
Config reload must also hand the replacement monitor's tenant-scoped guest,
|
||||
Docker, and host metadata stores to shared API/config persistence consumers.
|
||||
That handoff is lifecycle continuity only: it prevents post-reload agent
|
||||
reports and resource projection from observing an orphaned metadata cache, and
|
||||
does not add a report shape, credential, command, or remote-configuration
|
||||
authority.
|
||||
|
||||
@@ -6719,3 +6719,9 @@ status/history read remains bounded and cannot trigger a replacement run.
|
||||
`internal/api/ai_handlers_patrol_actions_additional_test.go`, and
|
||||
`frontend-modern/src/features/patrol/__tests__/patrolRunAcceptance.test.ts`
|
||||
are the focused acceptance, API, and cold-start timing proofs.
|
||||
|
||||
Assistant web-interface URL updates resolve metadata providers per organization
|
||||
from the active tenant monitor. Provider replacement refreshes both the default
|
||||
service and already-created tenant services after monitor reload, so no AI
|
||||
service retains an orphaned cache or writes a tenant's URL through another
|
||||
organization's guest, Docker, or host metadata store.
|
||||
|
||||
@@ -36,6 +36,7 @@ WWN only when the match is unique and the hardware identifier is usable.
|
||||
1. `internal/api/contract_test.go`
|
||||
1a. `internal/api/platform_connection_shared.go`
|
||||
1b. `internal/api/metadata_handlers_shared.go`
|
||||
1c. `internal/config/persistence_metadata_accessors.go`
|
||||
2. `internal/api/resources.go`
|
||||
3. `internal/api/discovery_handlers.go`
|
||||
4. `internal/api/alerts.go`
|
||||
@@ -1820,7 +1821,16 @@ a new API state machine, queue contract, or verification-accounting field.
|
||||
be forgotten. `internal/api/metadata_handlers_shared.go` owns the
|
||||
guest/docker metadata GET/PUT payload semantics (empty object instead of
|
||||
null, zero record instead of 404 — pinned by
|
||||
`TestContract_MetadataGetPayloadsUseZeroRecordsInsteadOf404`). Deploy
|
||||
`TestContract_MetadataGetPayloadsUseZeroRecordsInsteadOf404`). Guest,
|
||||
Docker, and host metadata handlers must resolve the active tenant monitor's
|
||||
live metadata stores. `ConfigPersistence`, export/import, entitlement usage,
|
||||
and reload-time router wiring must share those store
|
||||
instances rather than retaining independent in-memory caches of the same
|
||||
files; a successful API write must therefore be visible to backend resource
|
||||
projection and configuration export immediately. Assistant URL updates must
|
||||
resolve a provider per organization and refresh existing services after a
|
||||
monitor replacement; a default provider must not be shared across tenants.
|
||||
Deploy
|
||||
preflights and jobs share `handleDeployJobStatus` /
|
||||
`handleDeployJobEvents` in `internal/api/deploy_handlers.go`; recovery
|
||||
points series/facets share `parseRecoveryListPointsOptions`; docker and
|
||||
|
||||
@@ -221,6 +221,11 @@ data cannot wrap into a fabricated healthy value.
|
||||
58. `internal/monitoring/proxmox_action_observer.go`
|
||||
59. `internal/monitoring/agent_fleet_doctor.go`
|
||||
60. `internal/config/host_continuity.go`
|
||||
61. `internal/monitoring/docker_metadata_migration.go`
|
||||
62. `internal/monitoring/kubernetes_metadata_migration.go`
|
||||
63. `internal/monitoring/metadata_stores.go`
|
||||
63a. `internal/config/docker_metadata.go`
|
||||
63b. `internal/config/guest_metadata.go`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
@@ -1495,6 +1500,26 @@ Docker metadata and legacy app-container guest metadata to that key when a
|
||||
Docker report is ingested, then prefer that stable guest key when projecting
|
||||
unified app-container custom URLs. Runtime container IDs remain action and
|
||||
metric identities, not the persistent URL metadata identity.
|
||||
When a container retains its runtime ID but changes normalized name, monitoring
|
||||
must move stable guest and Docker metadata to the new name and remove the
|
||||
obsolete name key after a successful or already-resolved destination. Rename
|
||||
migration must snapshot all sources before writing so swaps do not exchange
|
||||
URLs accidentally, and ambiguous normalized source or target names must fail
|
||||
closed. A later unrelated container that reuses the old name must not inherit
|
||||
the renamed container's URL.
|
||||
Kubernetes pod, Deployment, and Service web-interface metadata uses
|
||||
`k8s-workload:<cluster>:<kind>:<namespace>:<name>` as its stable logical
|
||||
identity. Monitoring must migrate a current legacy unified-resource key, plus
|
||||
the legacy `k8s:<cluster>:pod:<pod-uid>` key for pods, when that resource is
|
||||
observed. Runtime UIDs remain discovery and metrics coordinates. Every scope
|
||||
component is required so a URL cannot cross cluster, namespace, or kind
|
||||
boundaries, and an existing empty stable record is an intentional clear that
|
||||
must block legacy fallback.
|
||||
The monitor-owned guest, Docker, and host metadata stores are the live
|
||||
in-memory authority for projection and migration. API, config export/import,
|
||||
tenant usage, Assistant URL discovery, and reload paths must share those exact
|
||||
tenant-scoped store instances rather than opening parallel caches over the
|
||||
same files.
|
||||
The same applies to proxmox topology coordinates exposed through typed views:
|
||||
node, cluster, and instance accessors must return canonical trimmed values so
|
||||
monitoring consumers do not fork topology grouping or labeling on `" pve-a "`
|
||||
|
||||
@@ -168,7 +168,8 @@ admission records a stable refusal without invoking executor or network code.
|
||||
WebSocket startup, so a delayed or blocked first stream snapshot cannot
|
||||
hide server-owned platform scopes; auth-mode branches must not fork
|
||||
additional state probes or perform the same hydration twice.
|
||||
17. `internal/api/slo.go` shared with `api-contracts`: the SLO endpoint is both an API contract surface and a protected performance hot-path boundary.
|
||||
17. `frontend-modern/src/utils/workloads.ts` shared with `unified-resources`: the stable workload metadata identity helper is both a unified-resource persistence boundary and a workloads hot-path lookup boundary.
|
||||
18. `internal/api/slo.go` shared with `api-contracts`: the SLO endpoint is both an API contract surface and a protected performance hot-path boundary.
|
||||
|
||||
Governed action decisions preserve SQLite and MemoryStore parity through one
|
||||
shared pure append command. Every accepted approval advances a monotonic
|
||||
@@ -1997,3 +1998,11 @@ The completed Operational Trust path preserves these explicit bounds:
|
||||
covered by query-plan and retention tests;
|
||||
6. Operational Trust telemetry uses bounded enumerated labels only and never
|
||||
introduces resource-cardinality labels.
|
||||
|
||||
Workload URL persistence remains lookup-only on the hot path. Docker and
|
||||
Kubernetes stable metadata candidates are derived synchronously from platform
|
||||
identity already present on each workload; the workload hook may carry the
|
||||
cluster scope needed for that calculation, but must not add per-row requests,
|
||||
runtime-UID scans, or a second metadata cache. Kubernetes persistence uses the
|
||||
fully scoped cluster, kind, namespace, and name identity; runtime pod UIDs are
|
||||
legacy fallback coordinates only.
|
||||
|
||||
@@ -507,6 +507,14 @@
|
||||
"unified-resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "frontend-modern/src/utils/workloads.ts",
|
||||
"rationale": "the stable workload metadata identity helper is both a unified-resource persistence boundary and a workloads hot-path lookup boundary",
|
||||
"subsystems": [
|
||||
"performance-and-scalability",
|
||||
"unified-resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "internal/agentcapabilities/action_target.go",
|
||||
"rationale": "the Pulse Intelligence governed action target type and resource-to-action-target mapping vocabulary are both the Assistant approval/runtime routing contract and the canonical API/agent target contract for governed actions",
|
||||
@@ -1880,6 +1888,7 @@
|
||||
],
|
||||
"exact_files": [
|
||||
"internal/api/ai_handler_test.go",
|
||||
"internal/api/ai_handlers_more_test.go",
|
||||
"internal/api/ai_handlers_patrol_actions_additional_test.go",
|
||||
"internal/api/ai_handlers_test.go",
|
||||
"internal/api/ai_intelligence_handlers_test.go",
|
||||
@@ -2462,6 +2471,7 @@
|
||||
"frontend-modern/src/utils/apiTokenPresentation.ts",
|
||||
"frontend-modern/src/utils/infrastructureSettingsPresentation.ts",
|
||||
"internal/api/setup_script_render.go",
|
||||
"internal/config/persistence_metadata_accessors.go",
|
||||
"internal/websocket/hub.go",
|
||||
"pkg/aicontracts/action_broker.go",
|
||||
"pkg/aicontracts/fix_execution.go",
|
||||
@@ -2807,6 +2817,20 @@
|
||||
"internal/api/alerts_endpoints_test.go"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "metadata-persistence-cache-authority",
|
||||
"label": "metadata persistence live-store authority proof",
|
||||
"match_prefixes": [],
|
||||
"match_files": [
|
||||
"internal/config/persistence_metadata_accessors.go"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
"test_prefixes": [],
|
||||
"exact_files": [
|
||||
"internal/api/metadata_handlers_test.go",
|
||||
"internal/config/persistence_metadata_test.go"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "backend-payload-contracts",
|
||||
"label": "backend API payload proof",
|
||||
@@ -2820,9 +2844,11 @@
|
||||
],
|
||||
"exact_files": [
|
||||
"frontend-modern/src/types/api.ts",
|
||||
"internal/api/ai_handlers_more_test.go",
|
||||
"internal/api/ai_handlers_patrol_actions_additional_test.go",
|
||||
"internal/api/contract_test.go",
|
||||
"internal/api/host_agent_removal_lifecycle_integration_test.go",
|
||||
"internal/api/metadata_handlers_test.go",
|
||||
"internal/api/patrol_autopilot_test.go"
|
||||
]
|
||||
},
|
||||
@@ -5132,6 +5158,8 @@
|
||||
],
|
||||
"owned_files": [
|
||||
"docker-entrypoint.sh",
|
||||
"internal/config/docker_metadata.go",
|
||||
"internal/config/guest_metadata.go",
|
||||
"internal/config/host_continuity.go",
|
||||
"internal/dockeragent/collect.go",
|
||||
"internal/dockeragent/docker_client.go",
|
||||
@@ -5200,6 +5228,7 @@
|
||||
"internal/config/host_continuity_test.go",
|
||||
"internal/monitoring/issue1485_unraid_lifecycle_test.go",
|
||||
"internal/monitoring/issue1595_collection_trust_test.go",
|
||||
"internal/monitoring/monitor_docker_test.go",
|
||||
"internal/monitoring/monitor_host_agent_removal_lifecycle_test.go",
|
||||
"internal/monitoring/monitor_host_agents_test.go",
|
||||
"internal/monitoring/monitor_package_updates_test.go",
|
||||
@@ -5528,10 +5557,15 @@
|
||||
"match_prefixes": [
|
||||
"internal/monitoring/"
|
||||
],
|
||||
"match_files": [],
|
||||
"match_files": [
|
||||
"internal/config/docker_metadata.go",
|
||||
"internal/config/guest_metadata.go"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
"test_prefixes": [],
|
||||
"exact_files": [
|
||||
"internal/config/docker_metadata_test.go",
|
||||
"internal/config/guest_metadata_test.go",
|
||||
"internal/monitoring/availability_poller_test.go",
|
||||
"internal/monitoring/availability_udp_test.go",
|
||||
"internal/monitoring/canonical_guardrails_test.go",
|
||||
@@ -5540,6 +5574,7 @@
|
||||
"internal/monitoring/monitor_alert_intent_test.go",
|
||||
"internal/monitoring/monitor_alert_override_migration_test.go",
|
||||
"internal/monitoring/monitor_backups_readstate_test.go",
|
||||
"internal/monitoring/monitor_docker_test.go",
|
||||
"internal/monitoring/monitor_host_agent_removal_lifecycle_test.go",
|
||||
"internal/monitoring/monitor_host_agents_test.go",
|
||||
"internal/unifiedresources/code_standards_test.go"
|
||||
@@ -7013,6 +7048,7 @@
|
||||
"frontend-modern/src/utils/serviceHealthPresentation.ts",
|
||||
"frontend-modern/src/utils/sourcePlatforms.ts",
|
||||
"frontend-modern/src/utils/sourceTypePresentation.ts",
|
||||
"frontend-modern/src/utils/workloads.ts",
|
||||
"frontend-modern/src/utils/workloadTypePresentation.ts",
|
||||
"internal/api/resources.go",
|
||||
"internal/platformsupport/manifest_generated.go"
|
||||
@@ -7183,6 +7219,7 @@
|
||||
"frontend-modern/src/routing/resourceLinks.ts",
|
||||
"frontend-modern/src/types/resource.ts",
|
||||
"frontend-modern/src/utils/resourceIdentity.ts",
|
||||
"frontend-modern/src/utils/workloads.ts",
|
||||
"internal/api/resources.go"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
@@ -7246,6 +7283,7 @@
|
||||
"frontend-modern/src/stores/__tests__/websocket-unified.test.ts",
|
||||
"frontend-modern/src/types/__tests__/resource.test.ts",
|
||||
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
|
||||
"frontend-modern/src/utils/__tests__/workloads.test.ts",
|
||||
"internal/unifiedresources/code_standards_test.go"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1551,3 +1551,10 @@ accepts only a non-empty reason and bounded future expiry. Action offer and
|
||||
planning require the `ai_autofix` entitlement in addition to current RBAC,
|
||||
evidence, resource-capability, executor-readiness, approval, and action-policy
|
||||
checks.
|
||||
|
||||
Guest, Docker, and host metadata reads, writes, and reloads must resolve the
|
||||
active request tenant's monitor-owned store. A default-tenant cache must never
|
||||
be reused to satisfy another organization, including Assistant-authored URL
|
||||
updates, and stable workload identities must retain their full host or cluster,
|
||||
namespace, kind, and name scope so a saved URL cannot cross a tenant or
|
||||
resource boundary.
|
||||
|
||||
@@ -2278,6 +2278,11 @@ fields and nullable mode/kind metadata before presenting canonical item labels,
|
||||
while storage detail drawers and filter controls must route summary series IDs,
|
||||
source tones, and disk metrics through the shared storage helpers instead of
|
||||
reconstructing them from local table state.
|
||||
Config export/import remains adjacent persistence infrastructure rather than a
|
||||
storage/recovery authority. Guest metadata import must reload the active
|
||||
tenant monitor's live store so recovery and resource projections observe one
|
||||
post-import state; it must not leave a parallel persistence cache or reload the
|
||||
default tenant on behalf of another organization.
|
||||
|
||||
Host-agent removal tombstones in `internal/config/host_continuity.go` are an
|
||||
adjacent agent-lifecycle security journal, not storage or recovery inventory.
|
||||
|
||||
@@ -128,6 +128,7 @@ life is derived.
|
||||
58. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts`
|
||||
59. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts`
|
||||
60. `frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts`
|
||||
60a. `frontend-modern/src/utils/workloads.ts`
|
||||
61. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerOperationalModel.ts`
|
||||
62. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts`
|
||||
63. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts`
|
||||
@@ -470,6 +471,15 @@ the resource name through the shared `WebInterfaceNameLink` primitive. Proxmox
|
||||
host table columns are governed by `proxmoxHostTableModel.ts`; that model must
|
||||
not reintroduce a separate `Web` column or move the web-interface launch back
|
||||
into a page-local icon cell.
|
||||
Persisted workload URLs must also use the same stable identity in the workload
|
||||
and infrastructure drawers. Docker app containers use
|
||||
`app-container:<host>:name:<normalized-name>`; Kubernetes pods, Deployments,
|
||||
and Services use
|
||||
`k8s-workload:<cluster>:<kind>:<namespace>:<name>`. Runtime container IDs,
|
||||
pod UIDs, and generated registry IDs remain fallback migration, action,
|
||||
discovery, or metrics coordinates and must not be the primary metadata write
|
||||
key. Missing cluster/host, namespace, kind, or name scope must fail closed to
|
||||
the legacy resource key rather than constructing a broadened stable key.
|
||||
Product-originated resource references may arrive as registered unified
|
||||
resource IDs, source-specific IDs, or canonical identity aliases. The
|
||||
unified-resource registry owns resolving those references through
|
||||
@@ -630,7 +640,8 @@ container inventory table.
|
||||
display/source family; `platformScopes` is the overlap set used when a
|
||||
runtime workload belongs to both Docker and an owning infrastructure
|
||||
platform.
|
||||
18. `internal/api/resources.go` shared with `api-contracts`: the unified resource endpoint is both a backend payload contract surface and a unified-resource runtime boundary.
|
||||
18. `frontend-modern/src/utils/workloads.ts` shared with `performance-and-scalability`: the stable workload metadata identity helper is both a unified-resource persistence boundary and a workloads hot-path lookup boundary.
|
||||
19. `internal/api/resources.go` shared with `api-contracts`: the unified resource endpoint is both a backend payload contract surface and a unified-resource runtime boundary.
|
||||
`/api/resources` type filters must accept URL-encoded comma-separated lists
|
||||
from browser query builders exactly like literal comma separators, so Docker
|
||||
/ Podman runtime pages do not lose `docker-host` inventory while requesting
|
||||
|
||||
Reference in New Issue
Block a user