Skip to content

feat(auth)!: log in to the Archgate platform and serve git credentials - #602

Merged
rhuanbarreto merged 27 commits into
mainfrom
feat/logto-device-login
Sep 7, 2026
Merged

rhuanbarreto merged 27 commits into
mainfrom
feat/logto-device-login

Conversation

@rhuanbarreto

@rhuanbarreto rhuanbarreto commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

archgate login now runs an OAuth 2.0 Device Authorization Grant (RFC 8628) against the Archgate platform instead of GitHub, and stores the resulting access and refresh tokens rather than a single long-lived plugin token.

Why a credential helper

Access tokens are short-lived, and the old storage model cannot carry that. Plugin repositories are cloned over HTTPS, and git reads the stored credential itself — the CLI is not in that request path. A token written once into the credential manager would stop working within the hour.

archgate credential closes the gap as a git credential helper: git asks it on each request, and it answers with a cached access token, renewing from the refresh token only once the cached one has lapsed. This is the same shape as gh auth git-credential for github.com, including the config idiom:

[credential "https://plugins.archgate.dev"]
	helper =
	helper = !archgate credential

The empty entry resets helpers inherited from a wider config scope so archgate is the only one consulted for that host alone; every other remote keeps whatever helper it already had. archgate login logout removes both entries.

Two details worth reviewing closely

Tokens are filed under auth.archgate.dev, not the plugins host. The store reaches the keychain through git credential, so keeping tokens under the host archgate is the helper for would make every read re-enter this process. Storing them elsewhere is what avoids the recursion.

Bun.spawn snapshots the environment at startup. A later Bun.env assignment does not reach the child, so git() passes env explicitly. Without it the helper tests silently wrote to the developer's real ~/.gitconfig while appearing to pass — the config assertions were reading back the same polluted file. I hit exactly that and had to clean my own global config.

Migration

Tokens issued before this change stay filed under the plugins host and are still returned unchanged, so existing sessions keep working until the user signs in again.

Removed

The GitHub device flow, the token claim call and the signup prompts are gone along with their tests — identity and registration now belong to the Archgate platform. SIGNUP_EDITORS went with them.

Not addressed here

Any account that can sign in to the Archgate platform now obtains plugin access. The signup step previously gated that. If the beta allowlist is still wanted, it needs an explicit check on the server rather than in the CLI — that is a backend change, not a CLI one, so it is deliberately out of scope for this PR. Flagging it because it is a policy change, not just a refactor.

Validation

bun run validate passes end to end: lint, typecheck, format:check, 2600 tests, archgate check 43/43, knip, build check.

New tests cover the device flow (polling, slow_down, denial, expiry, refresh rotation, expiry skew), the credential protocol parser and formatter — including that a value containing a newline is dropped rather than allowed to forge protocol lines — the helper's silence for hosts that are not ours, and the git config registration including the reset entry and idempotency across repeated logins.


BREAKING CHANGE: archgate login authenticates against the Archgate platform
via the OAuth device flow instead of GitHub's, so every user signs in again
to obtain a platform session.

  • Signup no longer happens in the CLI. The email, editor and use-case prompts
    are gone; registration is whatever the Archgate platform's sign-in offers.
  • Credentials are stored as an access/refresh token set under
    auth.archgate.dev, not a single token under plugins.archgate.dev.
  • archgate login writes a credential.https://plugins.archgate.dev.helper
    entry to the user's global git config, and archgate login logout removes it.
  • archgate credential is a new command, invoked by git rather than by users.

Tokens issued before this change keep working: they remain under
plugins.archgate.dev and are still returned, so existing sessions are not
invalidated until the user signs in again.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: d73bf716-b151-4293-a346-dba25c3b9496

📥 Commits

Reviewing files that changed from the base of the PR and between 6231c67 and 39b00e4.

📒 Files selected for processing (8)
  • docs/public/llms-full.txt
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
  • src/commands/login.ts
  • tests/commands/login.test.ts
  • tests/helpers/credential-store-renewal.test.ts
  • tests/helpers/credential-store.test.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Lint, Test & Check
  • GitHub Check: Smoke Test (Windows) / Windows
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (35)
This project uses Archgate — an AI governance framework based on Architecture Decision Records (ADRs).

⚙️ CodeRabbit configuration file

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • docs/public/llms-full.txt
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Every subcommand module under `src/commands//` — at any nesting depth — MUST have a corresponding heading in the top-level parent's reference page at `docs/src/content/docs/reference/cli/.mdx`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-016-cli-subcommand-documentation-accuracy.md)

Files:

  • src/commands/login.ts
Keep English content at the root and mirror every English MDX file in each locale directory (`pt-br` and `nb`) with the same relative path and filename; do not create orphan translations.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
All platform detection in `src/` must go through `src/helpers/platform.ts` (`isWindows()`, `isMacOS()`, `isLinux()`, `isWSL()`, or `getPlatformInfo()`); direct `process.platform` access and duplicated detection logic are forbidden outside `...

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)

Files:

  • src/commands/login.ts
All commands that operate on `.archgate/` project resources must use the shared `findProjectRoot()` from `src/helpers/paths.ts`; direct `process.cwd()` project-root resolution is prohibited except in `init`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-011-consistent-project-root-resolution.md)

Files:

  • src/commands/login.ts
Each command module must export a `register*Command(program)` function; each non-`index.ts` command file must define exactly one command.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-001-command-structure.md)

Files:

  • src/commands/login.ts
Every asynchronous command action must wrap its entire body—from the first statement through the last—in a try-catch boundary.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-012-command-error-boundaries.md)

Files:

  • src/commands/login.ts
In Commander.js command files, options requiring type narrowing beyond plain strings MUST use `new Option()` from `@commander-js/extra-typings` and register it with `.addOption()` instead of `.option()`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-008-typed-command-options.md)

Files:

  • src/commands/login.ts
**Directory structure mirrors `src/`** — `src/engine/runner.ts` is tested by `tests/engine/runner.test.ts`, so tests are discoverable by convention.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-005-testing-standards.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Use `test.each()` or `describe.each()` for the same assertion logic against multiple independent inputs.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-025-idiomatic-bun-test-parametrization-and-matchers.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Use Bun's built-in test runner (`bun test`) for all tests.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-005-testing-standards.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
All environment variable access in `src/` MUST use `Bun.env` instead of `process.env`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-014-prefer-bun-env.md)

Files:

  • src/commands/login.ts
All subprocess execution in Archgate source files must use `Bun.spawn` with array-based arguments; do not use `Bun.$` or import `$` from `bun`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md)

Files:

  • src/commands/login.ts
copilot-gated behavior must use `isCopilotAvailable()` (PATH check OR config dir exists), never `isCopilotCliAvailable()` alone.

📄 CodeRabbit inference engine (CLAUDE.md)

Files:

  • src/commands/login.ts
Every `inquirer.prompt(...)` call must be wrapped in `withPromptFix(() => ...)` imported from `src/helpers/prompt.ts`; keep the wrapper adjacent to the prompt invocation so automated checks can detect it.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-019-inquirer-prompt-fix.md)

Files:

  • src/commands/login.ts
Every call to `Bun.Glob#scan()` (`glob.scan(...)`) in source must pass `{ dot: true }` in its options object, including scans whose patterns do not explicitly target dot-directories.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-020-glob-scan-include-dotfiles.md)

Files:

  • src/commands/login.ts
Use `styleText(format, text)` from `node:util` for all colored CLI output; do not use raw ANSI escape codes or third-party color libraries such as chalk, kleur, or picocolors.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-003-output-formatting.md)

Files:

  • src/commands/login.ts
Use exit codes 0 for success, 1 for expected failures, 2 for internal errors, and 130 for user cancellation; do not use other exit codes.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-002-error-handling.md)

Files:

  • src/commands/login.ts
When implementing Windows-specific behavior, consider WSL; do not assume that `process.platform === "linux"` means a native Linux environment.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)

Files:

  • src/commands/login.ts
Markdown and MDX text content MUST NOT contain a backslash-escaped backtick.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-021-authored-text-integrity.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
Prefer Bun built-ins for file I/O, HTTP, globbing, testing, and subprocess execution; prefer `node:` built-in modules over npm alternatives when appropriate.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-006-dependency-policy.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Zod schemas are the single source of truth.

📄 CodeRabbit inference engine (CLAUDE.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Brazilian Portuguese translations must use correct diacritical marks, including characters such as `ã`, `ç`, `é`, `í`, `ó`, `ú`, `â`, `ê`, `ô`, and `à`.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Files:

  • docs/src/content/docs/pt-br/getting-started/installation.mdx
Norwegian translations must use Bokmål rather than Nynorsk, use informal `du`, and preserve correct characters such as `æ`, `ø`, and `å`.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-002-docs-i18n.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
Project-authored TypeScript comments must be concise, describe current behavior only, and never narrate history, relocations, refactors, or how the code came to be.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-004-concise-forward-only-code-comments.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Use MDX format for all documentation content pages under `docs/src/content/docs/`.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
Top-level command modules must follow the `src/commands/.ts` or `src/commands//index.ts` convention.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-015-cli-command-documentation-coverage.md)

Files:

  • src/commands/login.ts
The documentation site must be an Astro 5/Starlight project under `docs/`, separate from the CLI project with its own package manifest, TypeScript configuration, lockfile, and build pipeline.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
Invoke linting, formatting, and validation through package scripts (`bun run lint`, `bun run format`, `bun run format:check`, and `bun run validate`), rather than directly invoking tool binaries such as `bunx prettier`, `bunx oxfmt`, `npx e...

📄 CodeRabbit inference engine (.archgate/adrs/GEN-003-tool-invocation-via-scripts.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
Use `_resetAllCaches()` from `src/helpers/platform.ts` to simulate different platforms in tests rather than mocking `process.platform` directly.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-009-platform-detection-helper.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
**Barrel files and re-exports are forbidden.**

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-004-no-barrel-files.md)

Files:

  • src/commands/login.ts
Heavy runtime dependencies such as `inquirer`, `posthog-node`, and `@sentry/*` must be loaded with dynamic `import()` at their point of use, never through top-level static value imports.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-018-lazy-load-heavy-dependencies.md)

Files:

  • src/commands/login.ts
In Bun TypeScript source files, use `await Bun.file(path).json()` to read JSON files instead of `JSON.parse(await Bun.file(path).text())` or `JSON.parse(fs.readFileSync(...))`.

📄 CodeRabbit inference engine (.archgate/adrs/ARCH-010-prefer-bun-built-in-json-parsing.md)

Files:

  • src/commands/login.ts
Do not include the docs build in the CLI `validate` pipeline; docs build failures must not block CLI development or CI.

📄 CodeRabbit inference engine (.archgate/adrs/GEN-001-documentation-site.md)

Files:

  • docs/src/content/docs/nb/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/pt-br/getting-started/installation.mdx
  • docs/public/llms-full.txt
Every TypeScript source file in `src/` and `tests/` must begin with `// SPDX-License-Identifier: Apache-2.0` followed by `// Copyright 2026 Archgate`.

📄 CodeRabbit inference engine (.archgate/adrs/LEGAL-001-spdx-license-headers.md)

Files:

  • tests/helpers/credential-store-renewal.test.ts
  • src/commands/login.ts
  • tests/helpers/credential-store.test.ts
  • tests/commands/login.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: rhuanbarreto
Repo: archgate/cli

Timestamp: 2026-09-06T21:44:14.002Z
Learning: In this CLI, `saveTokenSet()` persists tokens through `git credential approve`, so storage security depends on the user's configured Git credential helper. The built-in `store` helper writes credentials in plaintext to `~/.git-credentials`; documentation must not guarantee OS credential-manager storage or no plaintext files.
Learnt from: rhuanbarreto
Repo: archgate/cli

Timestamp: 2026-09-07T00:19:37.666Z
Learning: In PR `#602` for `archgate/cli`, `archgate credential get` serves only the platform token set stored by `archgate login` through `resolveAccessToken()`. It never serves legacy plugin-host tokens issued before the platform authentication flow. Legacy tokens can remain usable for other CLI behavior only until the user signs in to the platform, and a refused refresh does not fall back to a legacy token.
📚 Learning: 2026-07-25T22:03:14.216Z
Learnt from: rhuanbarreto
Repo: archgate/cli PR: 501
File: .archgate/adrs/ARCH-002-error-handling.md:0-0
Timestamp: 2026-07-25T22:03:14.216Z
Learning: In Archgate boundary-wrapped CLI command actions (the handlers that rely on `handleCommandError()` for user-facing error output), expected-failure guards should signal user errors by throwing `new UserError(<message/details>)` rather than directly calling `logError()` followed by `exitWith(1)`. This keeps user-facing logging and the exit path centralized in `handleCommandError()`. For normal/computed command outcomes (e.g., `const exitCode = getExitCode(await runChecks(...))`), use `await exitWith(exitCode)` instead of calling `process.exit(exitCode)` so telemetry/Sentry flushing and outcome tagging still run.

Applied to files:

  • src/commands/login.ts
🪛 Betterleaks (1.8.1)
docs/public/llms-full.txt

[high] 2393-2393: Detected a potential hardcoded password literal, which may expose account credentials.

(generic-password)


[high] 4517-4517: Detected a potential hardcoded password literal, which may expose account credentials.

(generic-password)

🔇 Additional comments (4)
docs/public/llms-full.txt (1)

112-112: LGTM!

Also applies to: 347-347, 492-492, 1331-1341, 1366-1366, 1478-1488, 1610-1610, 1913-1913, 2280-2282, 2311-2321, 2380-2380, 2391-2401, 4467-4548, 4635-4648, 4718-4734, 4743-4743, 4754-4758, 5421-5421

docs/src/content/docs/getting-started/installation.mdx (1)

236-236: LGTM!

docs/src/content/docs/nb/getting-started/installation.mdx (1)

236-236: LGTM!

docs/src/content/docs/pt-br/getting-started/installation.mdx (1)

236-236: LGTM!


📝 Walkthrough

Walkthrough

The CLI replaces GitHub device-flow signup with Archgate OAuth 2.0 Device Authorization Grant authentication. It stores and renews token sets, registers a host-specific archgate credential Git helper, and supports get, store, and erase operations. Login, refresh, logout, project initialization, desktop actions, and plugin-host URL handling were updated. Tests cover authentication, credential storage, Git integration, protocol handling, desktop behavior, and command lifecycle changes. Documentation was updated in multiple languages.

Merge Risk: 🟡 Moderate · up to 39b00

Published privacy policies describe outdated authentication and identity-data handling. Correct the English, Norwegian, and Brazilian Portuguese policies before release.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 86.75% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 83 functions across 28 files. (4 skipped: 4…
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.
Title check ✅ Passed The title clearly summarizes the main changes: migration to Archgate platform authentication and addition of Git credential serving.
Description check ✅ Passed The description directly explains the OAuth migration, token storage, credential helper behavior, migration compatibility, removed GitHub flow, scope limits, and validation results.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cloudflare-workers-and-pages

This comment was marked as resolved.

@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage

Metric Value
Lines 99.9% (10873 / 10883)
Threshold 99.9% minimum — met
Platforms Linux + Windows

Full HTML report available in workflow artifacts.

Per-directory breakdown
Directory Coverage Lines
src/commands/ 100.0% 2265 / 2265
src/engine/ 100.0% 2618 / 2619
src/formats/ 100.0% 151 / 151
src/helpers/ 99.8% 5839 / 5848

coderabbitai[bot]

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

@rhuanbarreto rhuanbarreto changed the title feat(auth): sign in via device authorization and serve git credentials feat(auth)!: sign in via device authorization and serve git credentials Sep 6, 2026
coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai

This comment was marked as outdated.

@rhuanbarreto

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai

This comment was marked as outdated.

@rhuanbarreto

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@rhuanbarreto

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@rhuanbarreto

This comment was marked as outdated.

@rhuanbarreto rhuanbarreto changed the title feat(auth)!: sign in via device authorization and serve git credentials feat(auth)!: log in to the Archgate platform and serve git credentials Sep 6, 2026
cursor[bot]

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai

This comment was marked as outdated.

@rhuanbarreto

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@rhuanbarreto

This comment was marked as outdated.

rhuanbarreto and others added 18 commits September 7, 2026 09:25
`credential erase` cleared the whole token set. Git erases whenever the
credentials it was handed are rejected, so a single 401 — an access token
that slipped past the renewal skew, or a transient error from the plugins
host — destroyed the refresh token too and cost the user a full device-flow
login for a recoverable failure. Erase now expires the access token and keeps
the refresh token, so the next lookup renews.

`renew()` also ignored whether the rotated pair was persisted. The exchange
invalidates the old refresh token, so a failed write left the stored one dead
with nothing to replace it: the current request still succeeded and every
later one failed with no explanation. The failure is now reported, on stderr
so the credential protocol on stdout stays intact.

Both reported by Cursor Bugbot.

The credential-store suite passed 500 lines with the new cases, so the
renewal ones move to their own file.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…vice-flow adapter

The CLI now reaches the platform's sign-in service only through the
PlatformAuth contract in platform-auth.ts; oauth-device-flow.ts is one
implementation of it, configured with endpoints rather than knowing whose
they are. No provider is named anywhere in the code.

Behaviour-preserving tidy-ups on the way: one form-POST helper for the three
token requests, one runner for git credential approve/reject, one launcher
loop shared by the browser and clipboard helpers, and one sign-in tail
shared by login and login refresh.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…idation

- Token requests send redirect: "error" so a 307/308 cannot replay a device
  code or refresh token to another host.
- git credential fill ignores stderr instead of leaving an unread pipe.
- invalidateAccessToken reports whether the lapsed token set was stored.
- Docs describe credential storage as depending on the configured git
  credential helper, chain the manual VS Code install on curl succeeding,
  and list archgate credential in the CLI index and sidebar.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
The auth contract, credential store, helper config and credential command
import PLUGINS_HOST from plugin-install.ts, which also derives its API and
marketplace URLs from it. Sign-in no longer defines a host that belongs to
the plugins service.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…e without git

- credentialInput throws a UserError for a host, username or password holding
  a line break or NUL, so an ID-token claim cannot forge a second host= line.
- git() in git-credential-config returns 1 when git cannot be started, so
  login warns and logout hints instead of failing with an internal error.
- The device-code body is read through jsonBody, keeping a proxy's HTML page
  a user-facing error.
- LoginFlowResult.githubUser is accountName; the value is whatever the
  platform reports.
- The helper-config test proves the reset entry lands after a system-scope
  helper, not only that a global entry is replaced.
- The privacy policy states what the CLI itself reads from the sign-in result.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
While archgate is the helper for the plugins host, the lookup for that host
reaches archgate itself rather than the OS store, so a legacy token filed
there was never rejected and resurfaced after logout. Logout and refresh now
remove the helper entry first; logout still clears when that removal fails.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
The first access token is minted by the device-code exchange, which sent no
resource indicator; only refreshed tokens carried the plugins audience. The
exchange now repeats the resource (RFC 8707 §2.2).

Test suites clean their temp directories through safeRmSync instead of a
blanket catch.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Git started by an editor or a GUI client rarely inherits the shell PATH that
holds archgate, and the reset entry leaves no other helper for the plugins
host, so those clones got no credentials. The helper entry now names this
executable by absolute path, quoted for the POSIX shell git runs it through,
with backslashes turned into forward slashes for Git for Windows.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Renewal now tells three outcomes apart: a refused grant is a
SessionExpiredError, which the credential store treats as signed out; an
unreachable service or a non-grant failure is a plain UserError that
propagates with its cause, so the user is neither told to sign in again nor
shown an internal error. A repeat login with a stored platform session
re-registers the git credential helper in case the first write failed.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…ithout a shell

- saveTokenSet reads back the record for the account it wrote and accepts
  it only when the blob matches; a helper answering an older or another
  user's record no longer counts as persisted.
- clearCredentials reports whether every record was removed; logout fails
  with a hint instead of reporting success over a leftover token.
- A refused platform session ends the credential lookup; the legacy store
  is consulted only while no platform session is stored.
- TLS failures during sign-in go through handleCommandError as a UserError.
- Windows and WSL open URLs through rundll32 rather than cmd or PowerShell,
  which parse ? and & out of the verification URL.
- Suites created by this change restore spies with mock.restore().
- Docs: storage wording depends on the configured helper everywhere,
  headless wording names native Linux, and the legacy-token behaviour is
  stated as it is.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…failures

- requestDeviceCode reports an unreachable service as a UserError with its
  cause; pollForTokens retries after a transport failure until the code
  expires instead of aborting the sign-in.
- clearCredentials counts a fill timeout as a host it could not clear, and
  login refresh stops on a failed clear instead of signing in over it.
- The credential helper skips the git install check at startup: git started
  it, stdout is the protocol channel, and an installer must not run there.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
The record is stored for the account "archgate" with the display name inside
the JSON payload, validated by SessionSchema. Signing in as another user
replaces the record instead of leaving the old one beside it, every read
selects this record rather than whichever the helper answers first, and
logout removes the record that is actually in use. With a constant account
and a JSON payload nothing can carry a protocol line break, so the explicit
guard is gone.

Docs use sign-in wording for the beta notes and describe refresh the same
way in every locale; suites restore spies with mock.restore().

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
An empty username, name or email claim was accepted as the account name;
the stored session requires a non-empty one, so the record was written and
then dropped on the next read. Empty claims are now skipped like absent ones.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…late-landing winner

- As git's credential helper the CLI skips telemetry and Sentry startup,
  the update notice and the exit flushes, so git is not held on the network;
  the answer ends with the protocol's blank-line terminator.
- A renewal refused because a sibling process rotated the token re-reads
  the store a few times before giving up, since the winner writes its
  result only after its own exchange.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
…ered

While the helper still answers for the plugins host, clearing cannot see a
legacy token behind it, so a failed unregister now halts refresh before
anything is touched. Logout and refresh share the recovery hint.

Also type the session-record test helpers with the schema-derived TokenSet,
and say sign in rather than sign up on the installation page.

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

@rhuanbarreto I will review the latest changes in PR #602.


🧠 Learnings used
Learnt from: rhuanbarreto
Repo: archgate/cli

Timestamp: 2026-09-07T00:19:37.666Z
Learning: In PR `#602` for `archgate/cli`, `archgate credential get` serves only the platform token set stored by `archgate login` through `resolveAccessToken()`. It never serves legacy plugin-host tokens issued before the platform authentication flow. Legacy tokens can remain usable for other CLI behavior only until the user signs in to the platform, and a refused refresh does not fall back to a legacy token.

Learnt from: rhuanbarreto
Repo: archgate/cli

Timestamp: 2026-09-06T23:01:09.910Z
Learning: For the manual VS Code extension-installation snippet in `docs/src/content/docs/guides/vscode-plugin.mdx` and its Norwegian and Portuguese translations, use `curl --fail ... && code --install-extension archgate.vsix` followed by `rm -f archgate.vsix`. Do not use `set -euo pipefail` because users paste the snippet into interactive shells, and do not use `curl --remove-on-error` because Ubuntu 22.04 includes curl 7.81, which predates that option.

Learnt from: rhuanbarreto
Repo: archgate/cli PR: 602
File: docs/src/content/docs/nb/reference/privacy-policy.mdx:34-34
Timestamp: 2026-09-06T22:55:35.127Z
Learning: In archgate/cli, GitHub is the only sign-in method for Archgate’s identity provider because its connector is GitHub-only. Privacy-policy documentation must describe the GitHub-to-`auth.archgate.dev` authentication hop and the GitHub profile fields the platform receives, even though the CLI communicates only with the Archgate platform.

Learnt from: rhuanbarreto
Repo: archgate/cli PR: 602
File: docs/public/llms-full.txt:5421-5421
Timestamp: 2026-09-06T22:55:31.555Z
Learning: For `archgate/cli`, GitHub is the Archgate platform's only sign-in connector. `archgate login` uses the Archgate identity provider, but the provider authenticates users through GitHub and receives the GitHub profile fields listed in the privacy notice: name, email address, GitHub username, and avatar. The platform sends the welcome email during registration. Privacy documentation may describe this platform-level GitHub hop even though the CLI does not directly contact GitHub.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@rhuanbarreto
rhuanbarreto force-pushed the feat/logto-device-login branch from 6b5aae7 to 39b00e4 Compare September 7, 2026 07:25
@rhuanbarreto
rhuanbarreto merged commit ec154b1 into main Sep 7, 2026
25 checks passed
@rhuanbarreto
rhuanbarreto deleted the feat/logto-device-login branch September 7, 2026 07:42
@archgatebot archgatebot Bot mentioned this pull request Sep 7, 2026
rhuanbarreto pushed a commit that referenced this pull request Sep 7, 2026
# archgate

## [0.56.0](v0.55.0...v0.56.0)
(2026-09-07)

### ⚠ BREAKING CHANGES

* **auth:** `archgate login` authenticates against the Archgate
  platform
  via the OAuth device flow instead of GitHub's, so every user signs in
  again
  to obtain a platform session.

  - Signup no longer happens in the CLI. The email, editor and use-case
  prompts
  are gone; registration is whatever the Archgate platform's sign-in
  offers.
  - Credentials are stored as an access/refresh token set under
`auth.archgate.dev`, not a single token under `plugins.archgate.dev`.
  - `archgate login` writes a
  `credential.https://plugins.archgate.dev.helper`
  entry to the user's global git config, and `archgate login logout`
  removes it.
- `archgate credential` is a new command, invoked by git rather than by
  users.

  Tokens issued before this change keep working: they remain under
`plugins.archgate.dev` and are still returned, so existing sessions are
  not
  invalidated until the user signs in again.

  ---------

### Features

* **auth:** log in to the Archgate platform and serve git credentials
([#602](#602))
([ec154b1](ec154b1))

### Bug Fixes

* **plugin-install:** keep Copilot CLI output out of error logs
([#598](#598))
([2155ed7](2155ed7)),
closes [#595](#595)

---
This PR was generated with
[simple-release](https://github.com/TrigenSoftware/simple-release).

<details>
<summary>📄 Cheatsheet</summary>
<br>



You can configure the bot's behavior through a pull request comment
using the `!simple-release/set-options` command.

### Command Format

````md
!simple-release/set-options

```json
{
  "bump": {},
  "publish": {}
}
```
````

### Useful Parameters

#### Bump

| Parameter | Type | Description |
|-----------|------|-------------|
| `version` | `string` | Force set specific version |
| `as` | `'major' \| 'minor' \| 'patch' \| 'prerelease'` | Release type
|
| `prerelease` | `string` | Pre-release identifier (e.g., "alpha",
"beta") |
| `firstRelease` | `boolean` | Whether this is the first release |
| `skip` | `boolean` | Skip version bump |
| `byProject` | `Record<string, object>` | Per-project bump options for
monorepos |

#### Publish

| Parameter | Type | Description |
|-----------|------|-------------|
| `skip` | `boolean` | Skip publishing |
| `access` | `'public' \| 'restricted'` | Package access level |
| `tag` | `string` | Tag for npm publication |

### Usage Examples

#### Force specific version

````md
!simple-release/set-options

```json
{
  "bump": {
    "version": "2.0.0"
  }
}
```
````

#### Force major bump

````md
!simple-release/set-options

```json
{
  "bump": {
    "as": "major"
  }
}
```
````

#### Create alpha pre-release

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "alpha"
  }
}
```
````

#### Publish with specific access and tag

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "beta"
  },
  "publish": {
    "access": "public",
    "tag": "beta"
  }
}
```
````

### Custom Changelog Preamble

You can add custom markdown to the top of the changelog (right after the
version header) using the `!simple-release/set-preamble` command. The
markdown after the command line becomes the preamble.

```md
!simple-release/set-preamble

## What's new?

- The website was completely redesigned
- The new API gives you awesome possibilities
```

In a monorepo, pass the full package name after the command to target a
single package's changelog. Wrap the name in backticks so GitHub keeps
it as text instead of a mention:

```md
!simple-release/set-preamble `@your-org/core`

## Core changes

- New plugin system
```

Use one comment per package, plus one without a name for the whole
release.

### Access Restrictions

The commands can only be used by users with permissions:
- repository owner
- organization member
- collaborator

### Notes

- The last comment with `!simple-release/set-options` command takes
priority
- The last `!simple-release/set-preamble` comment per package takes
priority
- JSON must be valid, otherwise the `set-options` command will be
ignored
- Parameters apply only to the current release execution
- The commands can be updated by editing the comment or adding a new one


</details>

<!--
  Please do not edit this comment.
  simple-release-pull-request: true
  simple-release-branch-from: release
  simple-release-branch-to: main
-->

Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
rhuanbarreto added a commit that referenced this pull request Sep 7, 2026
## Why

#602 made `archgate login` write a
`credential.https://plugins.archgate.dev.helper` entry so `git clone` of
a plugin repository gets a fresh access token. `archgate doctor` only
reported a `logged_in` boolean, so when a plugin clone prompted for a
password there was nothing to tell the user which part of that chain was
broken.

## What

`doctor` now reports two more things under **Archgate**:

- **Session** — `platform`, `legacy` (a token issued before platform
sign-in, which the helper cannot serve), or `none`.
- **Git helper** — the state of the helper entry, with the command that
repairs it:

| Label | Meaning | Hint |
|---|---|---|
| `OK` | entry names the running binary, reset entry precedes it,
platform session stored | — |
| `MISSING` | platform session stored, no entry | `archgate login` |
| `not registered` | no entry, legacy or no session | `archgate login
refresh` for legacy |
| `STALE` | entry names another archgate binary (upgrade, reinstall
elsewhere) | `archgate login` |
| `SHARED` | reset entry missing or after archgate, so an inherited
helper may answer first | `archgate login` |
| `NO SESSION` | entry present, nothing stored for it to serve |
`archgate login` |

`--json` gains `archgate.session` and `archgate.credential_helper: {
registered, current, resets_inherited }`.

The read side (`inspectGitCredentialHelper`) lives beside
`register`/`unregister` in `git-credential-config.ts` and reads every
config scope in the order git consults them, since a system entry ahead
of archgate answers first regardless of the global file. A git that
cannot start reads as nothing registered.

## Deliberately not included

A live `git credential fill` probe. From `doctor` it would spawn git →
archgate → `resolveAccessToken`, which can refresh over the network, and
it would run the helper recursively inside the command diagnosing it.
The static read plus session kind covers every misconfiguration the
helper code can produce; a live probe is `git clone` itself.

## Verified

- `bun run validate` passes.
- Fire-tested against isolated `GIT_CONFIG_GLOBAL` files: clean, stale
path, missing reset, and `--json` shape, under a pseudo-TTY for the
console rendering.
- Docs updated in en, nb, pt-br; `llms-full.txt` regenerates from those.

---------

Signed-off-by: Rhuan Barreto <rhuan.barreto@gmail.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@archgatebot archgatebot Bot mentioned this pull request Sep 7, 2026
rhuanbarreto pushed a commit that referenced this pull request Sep 7, 2026
# archgate

## [0.57.0](v0.56.0...v0.57.0)
(2026-09-07)

### Features

* **doctor:** diagnose the git credential helper entry
([#603](#603))
([91d8a1d](91d8a1d)),
references [#602](#602)

### Bug Fixes

* **credential:** read git's request through the Node stdin stream
([#604](#604))
([add40a5](add40a5))

---
This PR was generated with
[simple-release](https://github.com/TrigenSoftware/simple-release).

<details>
<summary>📄 Cheatsheet</summary>
<br>



You can configure the bot's behavior through a pull request comment
using the `!simple-release/set-options` command.

### Command Format

````md
!simple-release/set-options

```json
{
  "bump": {},
  "publish": {}
}
```
````

### Useful Parameters

#### Bump

| Parameter | Type | Description |
|-----------|------|-------------|
| `version` | `string` | Force set specific version |
| `as` | `'major' \| 'minor' \| 'patch' \| 'prerelease'` | Release type
|
| `prerelease` | `string` | Pre-release identifier (e.g., "alpha",
"beta") |
| `firstRelease` | `boolean` | Whether this is the first release |
| `skip` | `boolean` | Skip version bump |
| `byProject` | `Record<string, object>` | Per-project bump options for
monorepos |

#### Publish

| Parameter | Type | Description |
|-----------|------|-------------|
| `skip` | `boolean` | Skip publishing |
| `access` | `'public' \| 'restricted'` | Package access level |
| `tag` | `string` | Tag for npm publication |

### Usage Examples

#### Force specific version

````md
!simple-release/set-options

```json
{
  "bump": {
    "version": "2.0.0"
  }
}
```
````

#### Force major bump

````md
!simple-release/set-options

```json
{
  "bump": {
    "as": "major"
  }
}
```
````

#### Create alpha pre-release

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "alpha"
  }
}
```
````

#### Publish with specific access and tag

````md
!simple-release/set-options

```json
{
  "bump": {
    "prerelease": "beta"
  },
  "publish": {
    "access": "public",
    "tag": "beta"
  }
}
```
````

### Custom Changelog Preamble

You can add custom markdown to the top of the changelog (right after the
version header) using the `!simple-release/set-preamble` command. The
markdown after the command line becomes the preamble.

```md
!simple-release/set-preamble

## What's new?

- The website was completely redesigned
- The new API gives you awesome possibilities
```

In a monorepo, pass the full package name after the command to target a
single package's changelog. Wrap the name in backticks so GitHub keeps
it as text instead of a mention:

```md
!simple-release/set-preamble `@your-org/core`

## Core changes

- New plugin system
```

Use one comment per package, plus one without a name for the whole
release.

### Access Restrictions

The commands can only be used by users with permissions:
- repository owner
- organization member
- collaborator

### Notes

- The last comment with `!simple-release/set-options` command takes
priority
- The last `!simple-release/set-preamble` comment per package takes
priority
- JSON must be valid, otherwise the `set-options` command will be
ignored
- Parameters apply only to the current release execution
- The commands can be updated by editing the comment or adding a new one


</details>

<!--
  Please do not edit this comment.
  simple-release-pull-request: true
  simple-release-branch-from: release
  simple-release-branch-to: main
-->

Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@rhuanbarreto
rhuanbarreto restored the feat/logto-device-login branch September 16, 2026 14:29
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