Skip to content

feat: store remote session validation verdicts - #6293

Merged
daviddanialy merged 2 commits into
mainfrom
daviddanialy/aim-204-validation-storage
Sep 11, 2026
Merged

feat: store remote session validation verdicts#6293
daviddanialy merged 2 commits into
mainfrom
daviddanialy/aim-204-validation-storage

Conversation

@daviddanialy

@daviddanialy daviddanialy commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

AIM-204, part 3 of 5, stacked on #6292.

  • remotesessions.RecordRemoteSessionValidation / SetRemoteSessionValidation: writes last_validated_at, validation_status, validation_reason (columns from feat: capture upstream identity from ID tokens on remote session grants #6104) with a compare-and-swap on updated_at, tenant-bound through the client row; unknown never overwrites a stored valid, enforced in the UPDATE.
  • Verdicts are cleared on every soft delete and on refresh: a new or gone token was never presented.
  • RemoteSessionState gains the row id, CAS token and verdict fields; UpstreamToken gains the RemoteSessionID/RemoteSessionUpdatedAt CAS pair.
  • ValidationOutcome lives in remotesessions next to RemoteSessionValidation; remotesessions already imports remotesessionmetrics, so the metric records the outcome as a string rather than importing it back.
  • Metric gram.remote_session.validation by outcome and issuer; gram.remote_session.id on refresh-failure logs.
  • No caller yet; the probe lands in part 4.

Summary by cubic

Stores live validation verdicts for remote session credentials so the consent page can show whether a stored token still works upstream. Part 3 of AIM-204; the probe that produces these verdicts lands in part 4.

  • Adds RecordRemoteSessionValidation, a tenant-bound writer with compare-and-swap on updated_at; the client row's project or organization scopes the write.
  • unknown never overwrites a stored valid, enforced in the UPDATE.
  • Clears verdicts on soft delete and refresh, since the new credential was never presented.
  • RemoteSessionState and UpstreamToken carry the row ID and CAS token.
  • Adds metric gram.remote_session.validation by outcome and issuer.
  • Adds gram.remote_session.id attribute to refresh-failure logs.

Written for commit e4f17da. Summary will update on new commits.

Review in cubic

@daviddanialy
daviddanialy requested a review from a team as a code owner September 10, 2026 22:36
@linear-code

linear-code Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

AIM-204

@changeset-bot

changeset-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e4f17da

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Running ultrareview automatically — This PR adds concurrency-sensitive verdict persistence with CAS on updated_at and 'unknown never overwrites valid' logic, clears verdicts on delete/refresh, and modifies core session state and token resolution paths—high-risk for subtle race conditions.. I'll post findings when complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultrareview completed in 5m 29s

All reported issues were addressed across 15 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread server/internal/remotesessions/validation_test.go Outdated
@blacksmith-sh

This comment has been minimized.

@bflad bflad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Thanks for moving ValidationOutcome into remotesessions. Taking a string on Validation.Record to keep the metrics package dependency-free is the right call over inverting that import.

I checked the invariants this rests on rather than just reading the diff, so recording what I verified. The CAS token is accurate: resolveUpstreamToken rebinds sess through validateAndRefresh, so RemoteSessionUpdatedAt is the post-refresh value, TouchRemoteSessionLastUsed writes only last_used_at, and there are no triggers on the table. SetRemoteSessionValidation deliberately leaving updated_at alone is what keeps a verdict write from colliding with the refresh CAS.

The invalidation rule also has no gaps. Every query that writes access_token_encrypted clears the verdict: UpsertRemoteSession in its DO UPDATE SET, UpdateRemoteSessionTokensIfUnchanged, and the soft-delete paths. Taking probedAt before the probe runs rather than at write time is what makes the last_validated_at <= @last_validated_at guard mean "started later wins" instead of "finished later wins", which is the behavior you want for overlapping probes.

Two minor things.

ValidationOutcomeRevoked cannot be stored. The type is documented as the closed set remote_sessions.validation_status stores, but RecordRemoteSessionValidation refuses revoked and nothing else writes the column. In the PR that defines the storage layer, a member of the declared closed set that the writer rejects reads as a contradiction. I would drop it and reintroduce it with AIM-261, when something can actually write it.

The user_session_issuers binding is absent. The sibling queries at lines 584, 602 and 653 pair this exact client tenant clause with a usi binding, and this one omits it. For a platform client, where both project_id and organization_id are NULL, the tenant clause admits any caller, so the effective guard becomes the (id, subject_urn, remote_session_client_id, updated_at) tuple. I think that is fine, since the caller reached the row through an issuer-scoped read and the field is display-only, and TestRecordRemoteSessionValidation_PlatformClient shows the behavior is deliberate. Worth a line in the query comment so the divergence from its siblings reads as a decision rather than an oversight.

Base automatically changed from daviddanialy/aim-204-upstream-exchange to main September 11, 2026 16:45
@daviddanialy
daviddanialy force-pushed the daviddanialy/aim-204-validation-storage branch from 2558772 to e4f17da Compare September 11, 2026 16:49
@blacksmith-sh

This comment has been minimized.

@daviddanialy
daviddanialy added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 0d9e507 Sep 11, 2026
79 of 83 checks passed
@daviddanialy
daviddanialy deleted the daviddanialy/aim-204-validation-storage branch September 11, 2026 17:08
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants