Skip to content

Send stable installation identity when pairing#137

Merged
glittercowboy merged 1 commit into
mainfrom
codex/stable-machine-identity
May 1, 2026
Merged

Send stable installation identity when pairing#137
glittercowboy merged 1 commit into
mainfrom
codex/stable-machine-identity

Conversation

@glittercowboy
Copy link
Copy Markdown
Contributor

@glittercowboy glittercowboy commented May 1, 2026

Summary

  • Stores a stable local daemon installation identity in the daemon config.
  • Sends installationId during pairing so cloud pairing can restore the same machine row.
  • Reports missing stable identity in gsd-cloud doctor and surfaces malformed config load errors.

Local Feedback

  • go test ./cmd ./internal/api ./internal/config
  • go test ./...
  • CodeRabbit review completed; correctness findings addressed.

Merge / Release Notes

  • Cloud PR: https://github.com/gsd-build/gsd-cloud/pull/416
  • The daemon can run against cloud versions that ignore installationId; full idempotent re-pairing is active when the cloud migration/API are deployed.
  • Release a daemon tag after merge so installers include the stable identity field.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

Adds persistent InstallationID support: new field in Config, generated via NewInstallationID/EnsureInstallationID, included in PairRequest and persisted on successful pairing, and validated by an added doctor check that reports InstallationID using shortID formatting.

Changes

Cohort / File(s) Summary
Config
internal/config/config.go, internal/config/config_test.go
Adds InstallationID to Config, implements NewInstallationID() and (*Config) EnsureInstallationID(). Tests cover persistence and idempotent ID generation.
Pair API
internal/api/pair.go, internal/api/pair_test.go
PairRequest gains optional installationId (omitempty). Tests updated to assert request contains expected installationId.
Login / Pairing
cmd/login.go, cmd/login_test.go
buildPairRequest now populates InstallationID (newly generated or from EnsureInstallationID() when config exists). Successful pairing persists InstallationID into saved config. Tests validate behavior and malformed-config failure.
Doctor command
cmd/doctor.go
Adds checkInstallationIdentity to doctor checks; verifies config can be loaded and InstallationID is non-empty, reporting success with shortID(cfg.InstallationID) and providing targeted fix hints on failure. Also uses shortID for machine paired display.

Sequence Diagram

sequenceDiagram
    participant User
    participant Login
    participant Config
    participant PairAPI

    User->>Login: start login
    activate Login
    Login->>Config: Load() (may be os.ErrNotExist)
    Config-->>Login: config or error

    alt config missing
        Login->>Login: NewInstallationID()
    else config exists
        Login->>Config: EnsureInstallationID()
        Config-->>Login: InstallationID (existing or newly generated)
    end

    Login->>PairAPI: POST /api/daemon/pair (includes installationId)
    activate PairAPI
    PairAPI-->>Login: success (pair response)
    deactivate PairAPI

    Login->>Config: Save config (persist InstallationID, MachineID, token)
    Config-->>Login: saved
    deactivate Login
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped and mined some bytes anew,
A tiny ID now sticks like glue,
Minted, saved, and checked with cheer,
Paired and healthy — hop, it's here! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately summarizes the main change: adding a stable installation identity mechanism sent during pairing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/stable-machine-identity

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
cmd/login_test.go (1)

97-114: ⚡ Quick win

Add a regression test for malformed config load errors.

Please add a case where $HOME/.gsd-cloud/config.json contains invalid JSON and assert buildPairRequest returns an error. This will lock in the intended behavior for non-NotExist load failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/login_test.go` around lines 97 - 114, Add a new test (e.g.,
TestBuildPairRequestErrorsOnMalformedConfig) that sets HOME to t.TempDir(),
creates a malformed config file in the same place the production loader expects,
then calls buildPairRequest("ABC234","test-host") and asserts it returns a
non-nil error (and no valid request). This mirrors
TestBuildPairRequestMintsInstallationIDForLegacyConfig setup (use
t.Setenv("HOME", t.TempDir())), but instead of using config.Save(), write
invalid JSON to the config file location so buildPairRequest triggers a JSON
decode/load failure and your test locks in the expected error behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/login.go`:
- Around line 123-126: The buildPairRequest function is swallowing all errors
from config.Load() by returning (req, nil); change it to propagate the actual
load error instead: when cfg, err := config.Load() returns a non-nil err, return
the current req value and that err (or wrap it with context) so callers can
handle malformed/permission/load failures instead of proceeding as if
successful; update any callers of buildPairRequest if they expect only a nil
error.

---

Nitpick comments:
In `@cmd/login_test.go`:
- Around line 97-114: Add a new test (e.g.,
TestBuildPairRequestErrorsOnMalformedConfig) that sets HOME to t.TempDir(),
creates a malformed config file in the same place the production loader expects,
then calls buildPairRequest("ABC234","test-host") and asserts it returns a
non-nil error (and no valid request). This mirrors
TestBuildPairRequestMintsInstallationIDForLegacyConfig setup (use
t.Setenv("HOME", t.TempDir())), but instead of using config.Save(), write
invalid JSON to the config file location so buildPairRequest triggers a JSON
decode/load failure and your test locks in the expected error behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 51c42a92-d842-4c77-8c52-3a6db70640f3

📥 Commits

Reviewing files that changed from the base of the PR and between 23f9af1 and 83aa10b.

📒 Files selected for processing (7)
  • cmd/doctor.go
  • cmd/login.go
  • cmd/login_test.go
  • internal/api/pair.go
  • internal/api/pair_test.go
  • internal/config/config.go
  • internal/config/config_test.go

Comment thread cmd/login.go
@glittercowboy glittercowboy force-pushed the codex/stable-machine-identity branch from 83aa10b to d1fe223 Compare May 1, 2026 20:23
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/login.go (1)

59-67: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist InstallationID before calling Pair().

Right now the ID is only written to disk after client.Pair(req) succeeds. If the request reaches the server but the response is lost, the process exits, or this later save fails, the next retry will mint/send a different InstallationID, which defeats the stable identity / idempotent re-pair goal of this change. Save the ensured/generated ID before the network call so retries reuse the same value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/login.go` around lines 59 - 67, The InstallationID must be persisted
before making the network Pair() call: create or update a config.Config with the
ensured/generated InstallationID (e.g., set cfg.InstallationID =
req.InstallationID or create cfg var) and call config.Save(cfg) prior to
invoking client.Pair(req), so retries reuse the same InstallationID; after
successful Pair() you can then update the remaining fields (AuthToken,
TokenExpiresAt, MachineID, RelayURL, ServerURL) and save again with
config.Save(cfg).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cmd/login.go`:
- Around line 59-67: The InstallationID must be persisted before making the
network Pair() call: create or update a config.Config with the ensured/generated
InstallationID (e.g., set cfg.InstallationID = req.InstallationID or create cfg
var) and call config.Save(cfg) prior to invoking client.Pair(req), so retries
reuse the same InstallationID; after successful Pair() you can then update the
remaining fields (AuthToken, TokenExpiresAt, MachineID, RelayURL, ServerURL) and
save again with config.Save(cfg).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd42c3e5-2ffd-4663-b17c-0f7c9e435677

📥 Commits

Reviewing files that changed from the base of the PR and between 83aa10b and d1fe223.

📒 Files selected for processing (7)
  • cmd/doctor.go
  • cmd/login.go
  • cmd/login_test.go
  • internal/api/pair.go
  • internal/api/pair_test.go
  • internal/config/config.go
  • internal/config/config_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/api/pair.go
  • internal/api/pair_test.go
  • cmd/doctor.go
  • internal/config/config.go

@glittercowboy glittercowboy merged commit 7fa9c88 into main May 1, 2026
3 checks passed
@glittercowboy glittercowboy deleted the codex/stable-machine-identity branch May 1, 2026 20:40
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