Skip to content

design-15: close mutation, scope and reconciliation gaps - #20

Merged
lr00rl merged 10 commits into
alpha/v0.2.2from
feat/scope-vpncore-substore
Jul 22, 2026
Merged

design-15: close mutation, scope and reconciliation gaps#20
lr00rl merged 10 commits into
alpha/v0.2.2from
feat/scope-vpncore-substore

Conversation

@lr00rl

@lr00rl lr00rl commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Outcome

Closes the final Design 15 server gaps after the stacked cache slice: canonical vpncore/substore scopes with directed delegation, reachable managed/adopted line-user writes, typed stale-plan approvals, canonical usage accounting, declared-edge precedence, sidecar preservation/reattach, serialized Sub-Store auto-sync status, and concurrency-safe Lines caching.

Release inputs

  • SDK: 4a318f246d23875cdd6448d96a62bd87bd67215c
  • Dashboard: a927c6cae45493233a4b5c648bf68bbcba8c5a03 (0.2.2-alpha.7)
  • Required server/plugin floor: 0.2.2-alpha.2

Verification

  • go test ./... (outside sandbox for httptest loopback)
  • go vet ./...
  • targeted go test -race for line users, metadata, cache, scopes, UUID restore, native usage, and Sub-Store auto-sync
  • git diff --check

Rollout

Merge after #19, then deploy server compatibility before canonical plugin manifests. Roll back plugin manifests first, then dashboard/server compatibility.

Constraint: Existing legacy proxy:* grants remain runtime-compatible during migration.
Confidence: high
Scope-risk: broad
Directive: Do not deploy canonical plugin manifests before this server compatibility bridge.
Tested: Full server test and vet suites plus targeted race coverage.
Not-tested: HK canary, deliberately deferred until the complete stack is merged and packaged.

@lr00rl
lr00rl changed the base branch from feat/lines-read-model-cache to alpha/v0.2.2 July 22, 2026 13:41
lr00rl added 10 commits July 22, 2026 06:42
design-15 §6: a sidecar-declared downstream_line_uuid now joins to the
downstream line's hash across the whole fleet — exact across machines
and immune to NAT or shared ports — and lands in both jump_edges and
the new declared_jump_edges provenance subset. Inferred (host,port)
edges stay as the fallback; unknown or self references resolve to no
edge.
design-15 §7 (no plugin merge): declared payload operator-target fields
may now carry secret://<key>, resolved from the plugin's own encrypted
vault before binding — so a saved Sub-Store endpoint can back
http.operator.do without the URL re-crossing the browser. Resolution
goes through plugin_host.go's new pluginSecretValue, the single
non-broker read path the vault isolation test permits.

Every committed vpn-core mutation (identity CRUD, bindings, rotation,
applied line-user changes) re-arms a 30s debounced trigger; on fire, if
the Sub-Store companion has a saved endpoint and autosync enabled, the
server invokes its import as the system actor with allow/deny audits.
Silent skip when disabled or inactive; no generic event bus (appendix C).
design-15 §8 server slice: applyProxyUsageSnapshot now folds on-box
u_<sha256(user|line_uuid)[:16]> counter names back into (line_hash_id,
proxy user id) rows before eligibility checks and monotonic diffing.
The per-user total advances through the existing UserBytes path, the
line granularity persists in line_user_bytes, and names that match no
(bound, migrated) pair degrade to ignored — never zero-filled.

Counters from users on multiple lines sum into one proxy-user total
while each line keeps its own bucket, so the three-dimensional usage
read model (node, line, user) lights up without a contract change.
ADR-004: the vpn-core profiles settings model gains singbox_stats_api
(saved/reported/normalized with the same host:port validation as
proxy_usage_xray_api), AgentLaunchConfig persists it (lattice-sdk
f2a9b0e), and the generated reconfigure command exports
LATTICE_SINGBOX_STATS_API.
design-15 D2: lines.sync_metadata queues a reviewed sidecar apply for
one node — Plan is the metadata document itself (secret-free, so review
shows the exact bytes), and the apply script re-verifies the plan hash
at execution, failing closed on drift. Queueing is idempotent per
(node, metadata sha).

Discovery change detection queues the same sync automatically (system
actor, own fingerprint map independent of the 6h audit throttle):
a node's line set changing across reports queues one pending approval;
unchanged or error reports queue nothing. Every byte still waits for an
operator's approval.
The unified Lines view used to rebuild fleet-wide on every call —
lines.get included a full rebuild plus a linear scan for a single hash.
A small cache now holds the built groups and a line_hash_id index,
rebuilt only after explicit invalidation: inventory ingest, proxy
inbound/user/profile writes, node writes, vpn-core identity mutations,
and apply outcomes. A 60s TTL is the documented safety net for a missed
edge path; operator-driven mutations all invalidate explicitly, so the
UI never reads stale after its own actions.
Split vpn-core and Sub-Store into canonical RBAC domains while keeping the legacy proxy grants as a one-way migration bridge. The compatibility matrix intentionally lets vpn-core front the existing native proxy API, but prevents Sub-Store from inheriting that authority.

Constraint: Existing proxy:read/admin assignments must continue to authorize migrated first-party manifests

Rejected: Treat substore scopes as proxy aliases | would let a publisher-only companion reach native proxy APIs

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Keep the asymmetric compatibility matrix until legacy proxy assignments have an explicit migration path

Tested: go test ./...; go vet ./...; git diff --check
Runtime compatibility must not become authority to mint scopes in another plugin domain. Separate assignment containment from request authorization so legacy proxy grants can migrate forward while canonical vpn-core and Sub-Store grants remain isolated.

Constraint: Legacy proxy grants require an equal-strength migration path into both canonical plugin domains.

Rejected: Reuse runtime authorization for scope assignment | symmetric compatibility would let canonical grants cross plugin boundaries.

Confidence: high

Scope-risk: narrow

Reversibility: clean

Directive: Keep CanDelegateScope directional even if runtime scope aliases evolve.

Tested: go test ./internal/rbac ./internal/server; go vet ./internal/rbac ./internal/server

Not-tested: Live migration against an already-issued legacy PAT
Close the cross-repository gaps that made advertised line-user, metadata, chain, Sub-Store, and accounting paths unreachable or partially successful. Both managed and adopted writes now use typed stale-plan approvals, canonical identity, bounded rediscovery, visible auto-sync health, collision-safe UUID recovery, and concurrency-safe read models.

Constraint: Legacy proxy grants and migrated ProxyUser rows remain compatible during the alpha transition.

Rejected: Reuse runtime scope aliases for delegation or approval | compatibility must not broaden authority.

Rejected: Mark approval applied before persistence/reconciliation | it creates false-success states.

Confidence: high

Scope-risk: broad

Reversibility: clean

Directive: Preserve typed approval binding, declared-edge precedence, and generation-guarded cache publication.

Tested: go test ./... outside sandbox; go vet ./...; targeted race suites for line users, metadata, cache, scopes, UUID restore, usage and autosync

Not-tested: HK on-node mutation and Sub-Store backend, deferred to canary
Pin the server module and container build inputs to the merged Design 15 SDK and dashboard commits so CI, release images, and local verification exercise one reproducible compatibility set.

Constraint: Server containers embed dashboard and SDK by exact ref.

Rejected: Keep feature-head refs | squash merges would make release inputs non-canonical.

Confidence: high

Scope-risk: narrow

Reversibility: clean

Directive: Report these exact refs with every alpha deployment.

Tested: go test ./...; go vet ./...; SDK and Dashboard merge commits verified through GitHub

Not-tested: Container build, pending server branch push and tag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant