Skip to content

feat(proto): credentials auth and default-password state core#471

Merged
ankitgoswami merged 20 commits into
mainfrom
feat/proto-creds-auth
Jun 17, 2026
Merged

feat(proto): credentials auth and default-password state core#471
ankitgoswami merged 20 commits into
mainfrom
feat/proto-creds-auth

Conversation

@ankitgoswami

@ankitgoswami ankitgoswami commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Switches the proto miner plugin from Ed25519 key-based authentication to username/password credentials with factory defaults of admin/proto. This foundation PR also introduces the server-side DEFAULT_PASSWORD pairing state and telemetry signal so the fleet can represent a rig that is paired and reporting metrics but still needs password rotation.

Stack: this PR is the base of the split stack. #480 is stacked on this PR and hardens the server-side command, telemetry, SQL, cache, plugin, and fleet-node boundaries for DEFAULT_PASSWORD; #482 is stacked on #480 and contains the hand-written Fleet UI remediation UX; #477 is also stacked on #480 and handles phase-2 fleet-node enrollment cleanup. The diff here is relative to main; UI affordances and normal-action gating are intentionally out of scope and live in #482. Phase 3 remains a future cleanup PR for deleting dormant key-auth code and schema.

How it works

The server continues dispatching miners by DriverName, but the proto driver now advertises basic_auth instead of asymmetric auth. Pairing builds a UsernamePassword secret, encrypts it through the existing credentials path, and reuses those credentials when reconnecting to the miner. If no credentials are supplied, the driver returns the proto defaults and the pairer tries those so factory-default rigs can be adopted without key provisioning.

The proto plugin client lazily logs in to the rig REST API, caches the access token, and retries once after a 401 to handle token expiry or out-of-band credential changes. The public status endpoint reports default_password_active; telemetry carries that bit back on DeviceMetrics, and the server reconciles it into PAIRING_STATUS_DEFAULT_PASSWORD. That state is paired-like for telemetry, snapshots, rollups, CSV, and enrichment, while password-change success clears it back to PAIRED.

Diagrams

flowchart TD
  P["Pairer"] --> C{"proto driver capabilities"}
  C -->|"basic_auth"| S["UsernamePassword secret"]
  S --> E["encrypted miner_credentials"]
  S --> D["proto plugin client"]
  D --> L["POST /api/v1/auth/login"]
  L --> T["cached access token"]
  D --> R["GET /api/v1/system/status"]
  R --> M["DeviceMetrics.default_password_active"]
  M --> Q["device_pairing.DEFAULT_PASSWORD"]
Loading
stateDiagram-v2
  [*] --> PAIRED: pair with valid credentials
  PAIRED --> DEFAULT_PASSWORD: telemetry sees default_password_active
  DEFAULT_PASSWORD --> PAIRED: password update succeeds
  PAIRED --> AUTHENTICATION_NEEDED: credentials rejected
  AUTHENTICATION_NEEDED --> PAIRED: re-pair with valid credentials
Loading

Areas of the code involved

Area / package / file What changed Why it matters for review
plugin/proto/internal/driver Driver API v2, basic_auth capability, default credentials, and credential bundle validation Main auth-shape migration for proto rigs
plugin/proto/pkg/proto and plugin/proto/internal/device Login/token cache, 401 retry, status/default-password reads, password-change client update Runtime auth lifecycle against the rig REST API
server/sdk/v1 and generated SDK outputs Adds DeviceMetrics.default_password_active and capability plumbing Wire/API contract between server and plugins
proto/fleetmanagement/v1 and server/migrations/000089_* Adds PAIRING_STATUS_DEFAULT_PASSWORD and DB enum migration Persistent representation of default-password remediation state
server/internal/domain/telemetry Reconciles the positive default-password signal from telemetry State source of truth during polling
server/internal/domain/command Persists proto password changes and clears default-password state on success Keeps stored credentials aligned with on-device changes
server/internal/domain/fleetmanagement, stores/sqlstores, buildings, sites, collection Treats DEFAULT_PASSWORD as paired-like for visibility/enrichment/CSV Keeps metrics visible while remediation is pending
server/fake-proto-rig and server/docker-compose.yaml Fake rig models credential login and default-password-gated mutation behavior Test double follows the firmware contract
client/src/protoFleet/api/generated/fleetmanagement/v1/fleetmanagement_pb.ts Regenerated from the enum change Generated; hand-written UI is deferred to #482

Key technical decisions & trade-offs

Decision Trade-off
Replace proto auth in place under the existing proto driver name Existing device rows keep working, but migration fallback has to handle key-era rows
Use default credentials as the adoption fallback Factory rigs pair smoothly, while rigs with changed passwords surface as auth-needed
Model default password as a pairing state instead of only a snapshot flag Requires a migration and paired-like handling, but gives command/server flows a durable remediation state
Detect default password from a positive status flag, not a telemetry failure Preserves telemetry visibility, but requires firmware/plugin support for the status bit
Keep hand-written Fleet UI out of this PR Smaller foundation review, but default-password UX is not fully visible until #482

Testing & validation

  • cd client && npm run lint
  • cd plugin/proto && ../../bin/go test ./... partially passed: internal/device, internal/driver, pkg/proto, and tests/unit passed; plugin/proto/tests failed because Docker closed the image-build connection locally.
  • cd server && ../bin/go test ./internal/domain/plugins ./internal/domain/telemetry ./internal/domain/command partially passed: plugins and telemetry passed; the broader command package hit local Postgres authentication failures for DB-backed tests.
  • git diff --check

Not fully covered locally: Docker/testcontainers and DB-backed integration tests need a working local Docker/Postgres setup and should be covered by CI or a local just dev smoke before merge.

@github-actions github-actions Bot added documentation Improvements or additions to documentation javascript Pull requests that update javascript code client server shared labels Jun 15, 2026
@ankitgoswami ankitgoswami force-pushed the feat/proto-creds-auth branch from 7e15c6d to a83f4b4 Compare June 15, 2026 23:12
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (0e1feae120058389b4e5113d28ca5310c357c0e3...22f22e4958732a2f6c659cb209eaa73f046ab8dc, exact PR three-dot diff)
  • Model: gpt-5.5

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: HIGH

Findings

[HIGH] DEFAULT_PASSWORD Miners Cannot Be Resolved for Password Remediation

  • Category: Auth | Reliability
  • Location: server/internal/domain/command/service.go:652
  • Description: The PR makes UpdateMinerPassword selectors include DEFAULT_PASSWORD miners, and telemetry scheduling also now treats DEFAULT_PASSWORD as paired-like. However, command execution calls minerService.GetMiner() before reaching UpdateMinerPassword, and the miner service still resolves devices through server/sqlc/queries/miner_service.sql, whose direct and fleet-node queries require dp.pairing_status = 'PAIRED'. A miner recorded as DEFAULT_PASSWORD therefore cannot be opened as a miner handle.
  • Impact: Newly paired Proto devices that are correctly marked DEFAULT_PASSWORD cannot be remediated through Fleet: password-update commands fail before reaching the plugin, and telemetry reconciliation cannot poll them back to PAIRED after an out-of-band password change. This leaves miners stuck with known factory credentials unless operators bypass Fleet.
  • Recommendation: Update the miner-resolution queries to include DEFAULT_PASSWORD where remediation/telemetry needs a handle, regenerate sqlc, and add tests for GetMinerFromDeviceIdentifier plus UpdateMinerPassword against a DEFAULT_PASSWORD device. If non-remediation commands must remain blocked, enforce that explicitly in command preflight or command handlers instead of making the device unresolvable.

Notes

No cryptostealing or pool-hijack behavior was found in the changed hunks. The protobuf additions use new field numbers and preserve reserved fields; generated outputs appear to be included.


Generated by Codex Security Review |
Triggered by: @ankitgoswami |
Review workflow run

@ankitgoswami ankitgoswami force-pushed the feat/proto-creds-auth branch from 674ba1f to 9296418 Compare June 16, 2026 16:02
@ankitgoswami ankitgoswami marked this pull request as ready for review June 16, 2026 18:24
@ankitgoswami ankitgoswami requested a review from a team as a code owner June 16, 2026 18:24
Copilot AI review requested due to automatic review settings June 16, 2026 18:24

This comment was marked as low quality.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f5d9fe814

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread plugin/proto/pkg/proto/client.go
Comment thread proto/fleetmanagement/v1/fleetmanagement.proto
chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@ankitgoswami ankitgoswami force-pushed the feat/proto-creds-auth branch from 5d857b3 to a6cd903 Compare June 17, 2026 00:15
chatgpt-codex-connector[bot]

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

ankitgoswami and others added 19 commits June 17, 2026 08:44
Switch the proto plugin from Ed25519 key-based auth to username/password
credentials (default admin/proto), bumping the driver to API version v2.
The server is capability-driven, so dropping CapabilityAsymmetricAuth routes
proto through the existing username/password pairing, encrypted credential
persistence, and reconnect paths. Devices paired under v1 (no stored creds)
fall back to the factory defaults.

Default-password handling: rigs on the factory password report telemetry
normally (reads are not gated), and are surfaced as a distinct DEFAULT_PASSWORD
pairing state via the positive default_password_active flag on
GET /api/v1/system/status — plumbed through DeviceMetrics. The telemetry poll
reconciles the state with an in-memory transition guard; UpdateMinerPassword
clears it back to PAIRED. The frontend shows the device's metrics alongside a
"change password" remediation prompt.

Phases 2 (fleet-node creds-only) and 3 (remove key-auth code/columns) follow
in separate PRs; this phase leaves the dormant asymmetric-auth code in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- telemetry: use a checked type assertion in reconcileDefaultPasswordState
  (golangci-lint forcetypeassert).
- asicrs: set default_password_active on the DeviceMetrics initializer in
  device.rs (new proto field); fixes asicrs and plugin-contract builds.
- command: a credential-persistence failure after an on-device password change
  now fails the command instead of reporting false success, so Fleet never
  keeps stale credentials silently. (Codex security finding)
- fleetmanagement: treat DEFAULT_PASSWORD devices as paired-like for snapshot
  status/metadata and telemetry enrichment so their telemetry is shown.
  (Codex security finding)
- tests: collectPairedDeviceIdentifiers covers DEFAULT_PASSWORD; update the
  password-persistence device-type doc test for Proto.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egression test

Address the full scope of the Codex review findings:

- fleetmanagement: centralize the paired-like check (isPairedLikeStatus /
  isPairedLikePairingStatus) covering PAIRED and DEFAULT_PASSWORD, used by snapshot
  status/metadata and telemetry/group/rack enrichment.
- CSV export: DEFAULT_PASSWORD devices now export their telemetry values (telemetry
  is not gated by the default password); only AUTHENTICATION_NEEDED blanks values.
  Status stays "Needs attention" and the issue column shows "Password change required".
- command: add TestExecuteCommand_UpdateMinerPassword_PersistFailureFailsCommand —
  a regression test proving a persist failure after the on-device change fails the
  command instead of silently succeeding.
- tests: DEFAULT_PASSWORD cases for the CSV status/issues/value helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address three reliability/auth review findings:

- Presence-aware default-password signal: default_password_active is now an
  optional bool (SDK + V2 metrics carry *bool). The plugin sets it only on a
  successful /system/status read; the telemetry poll skips reconciliation when it
  is unset, so a transient probe failure or an older plugin can no longer demote a
  DEFAULT_PASSWORD device back to PAIRED.
- Pair-time remediation state: PairDevice reports the default-password flag, and
  the pairer records DEFAULT_PASSWORD with a non-ACTIVE initial status immediately
  instead of marking a factory-password rig fully paired/active until first poll.
- Reject empty pairing passwords: Client.Authenticate and the server secret-bundle
  creation reject empty passwords; the plugin's secret extraction no longer upgrades
  an explicit empty password to the factory default (nil bundle still falls back).

asicrs DeviceMetrics/DeviceInfo set the optional field to None. Adds regression
tests for the nil-signal, pair-time DEFAULT_PASSWORD, and empty-password paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The static sqlc queries still scoped to PAIRED + AUTHENTICATION_NEEDED, unlike the
dynamic rollups, so DEFAULT_PASSWORD devices were inconsistently handled:

- GetMinerStateCountsByDeviceIDs (site/building stats) + InsertMinerStateSnapshot /
  GetTotalMinerStateSnapshots now bucket DEFAULT_PASSWORD as broken/needs-attention,
  matching the dynamic CountMinersByState logic, instead of dropping it from every
  state bucket while still counting it present.
- Reconciliation lookups (GetPairedDeviceByMACAddress / ...ByMACAddresses /
  ...BySerialNumber) include DEFAULT_PASSWORD, so a default-password device that
  moves IP/subnet is matched to its existing row on rediscovery instead of failing
  on a duplicate insert or splitting state.
- GetAllPairedDeviceIdentifiers (telemetry poll seed) includes DEFAULT_PASSWORD, so
  those devices keep polling after a restart and can reconcile back to PAIRED.
- GetTotalPairedDevices / GetAvailableModels / GetAvailableFirmwareVersions /
  GetKnownSubnets include DEFAULT_PASSWORD for fleet-view consistency.

Adds integration tests for the by-device-IDs state counts and MAC reconciliation
of a DEFAULT_PASSWORD device. Auth-needed-only counts and the status mutation are
intentionally unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Condense the explanatory comments added across the credentials-auth work to
concise WHY-only notes, matching the repo's minimal-comment style. No behavior
change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route AUTHENTICATION_NEEDED / DEFAULT_PASSWORD checks through the
pairingRemediation helpers instead of inline enum comparisons, share the
"Password change required" label, and drop a duplicated sleeping-state
computation in StatusModal. reconcileDefaultPasswordState now writes on the
first determined reading so a device whose password changed while the server
was down is corrected on the next poll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DEFAULT_PASSWORD does not suppress telemetry; only AUTHENTICATION_NEEDED
does. Clarify that the two states differ in both telemetry behavior and
remediation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main merged 000087_notification_history_keyset_index and
000088_seed_notification_permissions while this branch was open, colliding
on 000087. Bump to the next free version so golang-migrate stops failing on
the duplicate file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ankitgoswami ankitgoswami force-pushed the feat/proto-creds-auth branch from 4948b2b to 33f98ff Compare June 17, 2026 15:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 33f98ff719

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ankitgoswami

ankitgoswami commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Re: Codex security review: This is addressed in the stacked child PR #480 rather than in the #471 diff: #480 adds paired-like miner resolution for DEFAULT_PASSWORD devices via include_default_password, routes UpdateMinerPassword through GetMinerForCredentialRemediation, routes telemetry through GetMinerForTelemetry, and keeps non-remediation commands explicitly guarded from DEFAULT_PASSWORD devices.

I am leaving #471 unchanged here so the stack stays as currently split: #471 carries the core DEFAULT_PASSWORD/auth state, and #480 carries the remediation/telemetry resolver hardening.

Comment thread server/internal/domain/command/execution_service.go
Comment thread server/internal/domain/command/service.go
Comment thread server/internal/domain/command/service.go
Comment thread server/internal/domain/telemetry/service.go
Comment thread server/internal/domain/command/service.go
Comment thread plugin/proto/internal/device/device.go
@ankitgoswami ankitgoswami merged commit ed936e0 into main Jun 17, 2026
84 checks passed
@ankitgoswami ankitgoswami deleted the feat/proto-creds-auth branch June 17, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client documentation Improvements or additions to documentation javascript Pull requests that update javascript code server shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants