Skip to content

Integrate connector ownership with Edge claims and independent expiry #976

Closed

Description

@privateip

Parent: #970

Deliverable

A provider-side ownership adapter that uses foundation instance leases and per-prefix claims while enforcing one active MASQUE session per connector.

Dependencies

#972, #975; foundation #997 and #998 for integrated route/withdrawal evidence.

Galactic Edge integration (#992)

Implement this milestone as a consumer of the Galactic Edge provider contract. #992 owns provider authorization, host attachment lifecycle, shared-state fencing and maintenance, independently enforced forwarding expiry, prefix conflict arbitration/publication, and gVPC traffic policy. MASQUE owns client authentication, connector /96 assignment and retention, session packet checks, transport behavior and its provider adapter. Use the foundation's versioned contract and existing provider helpers; do not introduce a second host-networking controller, lease store or BGP publisher. Any required foundation change must be tracked and qualified under #992 before this milestone claims integrated acceptance.

Work

  • Use the existing provider InstanceStore and route claim/release helpers with UID/resourceVersion preconditions, new process identity on restart and the contract timing parameters. Do not add a MASQUE host fence or parallel claim store.
  • Treat a written claim as intent only: forwarding requires the accepted attachment/instance/route epoch and foundation readiness/gate state. Replacements obey closed-epoch confirmation or the contract takeover wait.
  • Serialize same-instance connector sessions across both transports with an explicit session generation. Stop old packet I/O and discard old queued traffic before replacement activation; stale Close/release cannot affect a new session or claim.
  • Implement Define connector admission, addressing and ownership contracts #972's connector-specific credential/revocation enforcement. Continued renewal for other connectors must not extend revoked session authority; any necessary foundation extension is an explicit prerequisite.
  • Preserve independent foundation expiry while the provider, agent or API stalls. Release only the affected route on a single disconnect, preserving the instance lease and attachments used by other connectors.

Acceptance criteria

  • Packet tests cover provider crash/pause, agent pause, API partition with client/VPC traffic still available, restart with cached authority, node container restart and cross-node replacement.
  • Same-pod and cross-pod HTTP/3-to-HTTP/2 reconnects never have two forwarding sessions for one connector; delayed cleanup and buffered packets cannot affect the replacement.
  • Measure local cutoff, local withdrawal and remote convergence separately against Define the internal provider contract #993 bounds/assumptions. Core controls gates and route withdrawal independently of session cancellation.
  • Revoke or expire one connector while another keeps the shared instance renewing: the first stops within its documented bound and the other continues. Include stalled provider control handling and authority-source outage cases from Define connector admission, addressing and ownership contracts #972.
  • Tests prove stale renewals, claim writes and release attempts cannot reopen expired authority or damage replacement routes; resolve CONNECT-IP server lacks overload limits and tenant isolation checks #991's unfenced-backend finding before exposure.

Scope follows #970: CONNECT-IP with HTTP/2 fallback; no public API development, plugin/portal integration, product-client development or production rollout.

Activity

  1. changed the title [-]MASQUE: enforce per-connector ownership and bounded local expiry[/-] [+]Enforce per-connector ownership and bounded local expiry[/+] on Oct 22, 2026
  2. added theissue type on Oct 22, 2026
  3. changed the title [-]Enforce per-connector ownership and bounded local expiry[/-] [+]Integrate connector ownership with Edge claims and independent expiry[/+] on Oct 25, 2026
  4. self-assigned this
    on Oct 27, 2026
  5. privateip commented on Oct 27, 2026

    @privateip
    CollaboratorAuthor

    Verdict: valid. The adapter from #975 still lacks the session ownership this issue asks for.

    Checked on main at d2ef1ad:

    1. A connector can have only one request. Admit refuses a second request for the same connector with 503 while the first is open (internal/edge/masque/adapter.go, connectorOpen). TestDuplicateRequest asserts that refusal and passes. A reconnect on the same pod, including an HTTP/3 to HTTP/2 fallback, has to wait until the old stream closes. The adapter has no replacement path, no I/O barrier and no 1 s quiesce bound.
    2. A grant's deadline is fixed when the request is admitted. Authorizer has only Authorize (internal/edge/masque/grant.go), and each session arms one timer at Grant.NotAfter. The adapter cannot refresh a grant or revoke one connector. Under the #972 contract, a grant lasts at most 30 s, so every session would end at that ceiling.
    3. The foundation side already exists. The agent caps each route's gate at its service's validUntil (internal/edge/agent/gate.go), so one connector's revoked service closes only that connector's gate. This issue needs no foundation change.

    Already in place from #975: instance lease and route claims through internal/edge/provider with UID preconditions, admission gated on the accepted epoch, stale Close that cannot release a successor's claim, and per-connector route release.

    Still open in other issues:

  6. privateip commented on Oct 27, 2026

    @privateip
    CollaboratorAuthor

    Fix plan (approved):

    1. Same-pod replacement. Admit authenticates a connector's new request before it touches the open one. It then ends the old session and waits up to 1 s for that session's packet I/O to stop and its lease to close. If the old session does not stop in time, the new request fails and the adapter retires the connector's authorization. The attachment and route references stay held during the handoff, so the claim is not released and taken again. Close and release calls compare the session ID, so a late call from the old session cannot affect its successor. A third request arriving during a handoff gets a retryable 503.
    2. Refresh and revocation. Authorizer gains Refresh and Retire, and Adapter gains Revoke(connector). A refresh worker per session extends the session's deadline only when the refreshed grant keeps the same connector, VPC, service and /96. An unauthenticated or forbidden answer ends only that connector's session. When the authority is unavailable, the session keeps its last deadline and expires on it. The agent still closes the gate at the service's validUntil, so this does not replace the foundation ceiling.
    3. CONNECT-IP server lacks overload limits and tenant isolation checks #991 finding 5. Document the masque adapter as the fencing wrapper around TUNBackend.

    Tests:

    • Unit and envtest tests cover replacement, the I/O barrier, stale close, refresh and revoke, and revoking one connector while another keeps the shared instance lease renewed.
    • New gvpc lab rows cover same-pod reconnect, provider SIGSTOP, agent pause, API partition, process restart and single-connector revocation. They report local cutoff and local withdrawal as separate measurements.

    Not in this PR: the HTTP/2 rows (#981), cross-node replacement (needs #977's retire flow) and node reboot. The PR will reference this issue without closing it.

  7. privateip commented on Oct 27, 2026

    @privateip
    CollaboratorAuthor

    #1162 merged. It adds same-pod connector replacement, per-connector grant refresh and revocation, and five ownership rows in the MASQUE lab suite. #1171 fixes two defects in those rows that their first run exposed.

    At #1171's head, setup and all five rows pass in the lab:

    • Same-pod replacement: the replacement answered 500 of 500 probes. The replaced request's last reply came 15 ms before the replacement's first, so the two never forwarded together. The route kept its claim epoch.
    • Provider stall, agent stall and API partition with client traffic still flowing: local cutoff came 14.98 to 14.99 s after the last renewal start, inside the 15.52 s bound.
    • Local withdrawal, measured separately by polling the route's advertisement every 200 ms: 164 to 323 ms after the cutoff.
    • Revoking one connector: its service expiry stopped it 2.02 s before validUntil, matching the 2 s MaxSkew cap. The other connector on the same instance answered 1500 of 1500 probes, and the instance kept renewing at the same epoch.

    Still open on this issue:

  8. privateip commented on Oct 29, 2026

    @privateip
    CollaboratorAuthor

    Every acceptance criterion now has lab evidence, so I'm closing this. The adapter work merged in #1162 and #1171. The remaining rows merged in #1238 and #1240, and all of them passed on the containerlab lab at those PRs' heads.

    Criterion Lab evidence
    Provider crash and pause, agent pause, API partition The ownership rows from #1162 and #1171
    Restart with cached authority The process-restart row (#1238) retook the lease after 17.2 s; both connectors were back on their adopted attachments after about 20 s
    Node container restart The node-restart row (#1240) withdrew the route after 2.58 s. The new process claimed it only after the old lease expired
    Cross-node replacement, HTTP/3 to HTTP/2 The old pod's last reply came 32.6 s before the new pod's first, so the two never forwarded together
    Same-pod replacement, HTTP/3 to HTTP/2 The handoff gap was 19 ms. No stale packets reached the new request, and the claim epoch did not change
    Local cutoff, local withdrawal, remote convergence The cutoff came at 14.99 s. Local withdrawal followed 306 ms after the cutoff, and the route left the compute node's RIB 154 ms after it, against a 5 s target
    Revoke one connector while another renews Revoked: last reply 2.02 s before expiry. Disabled grant: stopped 348 ms after the disable, against a 7 s bound. The other connector answered every probe
    Stale renewals and releases cannot reopen authority Session-ID checks and their tests in #1162. #991's unfenced-backend finding was resolved before this closed

    One related bug stays open under #970: after an authority outage, the authority can renew a grant that core already expired (#1236). Authority still ends on time; the connector then gets 503 for about 30 s before it recovers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

Fields

Priority

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions