← Back to home@amosogra

dsh-subscription-signin

Sign in to ChatGPT, Claude, Copilot and other subscription model routes inside the DeepSeek Harness — the sign-in button the Harness never shipped.

Stars
0
Language
JavaScript
Created
Oct 12, 2026
Updated
Oct 13, 2026

Introduction

dsh-subscription-signin

tests

The sign-in button the DeepSeek Harness never shipped.

Use the AI subscriptions you already pay for — ChatGPT Plus/Pro, Claude Pro/Max, GitHub Copilot, and more — as model routes inside the DeepSeek Harness. Adds a Subscriptions settings page plus a compact extension on every provider card in Settings → Models.

Settings → Subscriptions → ChatGPT (Codex) → [ Sign in with ChatGPT (Codex) ]
                                    ↓
                    github.com/login/device  ·  code ABCD-1234
                                    ↓
                    ✓ Signed in — draws on your plan

No password is ever collected or stored by this plugin. The vendor runs the sign-in on its own page; the Harness credential store writes the token.

Repository layout

The installable package is the dsh-subscription-signin/ directory. The tests, license and documentation sit beside it and are not part of the package:

dsh-subscription-signin/   ← the package; this is what you install
  host.js                  ← the Host half (tool + HTTP route)
  client.js                ← the Client half (settings page + provider card)
  cordis.patch.yml         ← the accounts, as configuration
  locale/en.json           ← package metadata strings
  package.json
smoketest/                 ← five dependency-free suites (run from the root)
docs/FINDINGS.md           ← what was measured vs inferred

Why this exists

The Harness already ships everything needed to use a subscription as a model route. Its LLM adapter registers OAuth flows for eight providers — the transport works, the token exchange works, the credential store works.

What it has no surface for is starting one. Nothing in the GUI or CLI ever calls the authorization seam's begin(). This plugin is that missing surface.

It implements no OAuth. It does not touch PKCE, state, device codes, or token parsing — all of that lives in @earendil-works/pi-ai, shipped by DeepSeek. This plugin asks the Harness to begin a flow, shows you the URL and code, and reports the result.

Supported providers

Configured out of the box, driven through the Harness's own authorization seam:

ProviderCredentialLogin style
ChatGPT (Codex)llm-pi-ai/openai-codexdevice code
Claude (Pro/Max)llm-pi-ai/anthropicOAuth
GitHub Copilotllm-pi-ai/github-copilotdevice code
xAIllm-pi-ai/xaidevice code
Metallm-pi-ai/metadevice code
OpenRouterllm-pi-ai/openrouterOAuth + paste fallback
Radiusllm-pi-ai/radiusbrowser callback
Kimi For Codingllm-pi-ai/kimi-codingdevice code

Honest status. Sign-in is verified end to end for ChatGPT (Codex) and Claude (Pro/Max); Claude's route was observed authenticating successfully and then being rate-limited by the account (HTTP 429 with Anthropic's own request_id — a quota answer, not a credential failure). Copilot's flow is verified and completes. xAI, Meta, OpenRouter, Radius and Kimi hold credentials that have been written successfully, but no request has been served on those routes in the session where this was verified, so treat them as untested rather than broken. docs/FINDINGS.md records what is measured versus inferred.

Install

Requirements. DeepSeek Harness 0.2.0-rc.2 or later. Developed and verified against 0.2.0-rc.2, the release this plugin's two seams were checked on (ctx.authorization from @deepseek-ai/dsh-authorization, and the client slots service). Every Harness service it consumes is reached with a defensive ctx.get(...) plus an undefined check, so a missing one degrades to a reported message instead of a crash — but a Harness that has moved a seam will still need a matching plugin release.

The plugin is installed into a Harness profile as a bundle. Point the plugin manager at this directory:

install_bundle: link:/absolute/path/to/dsh-subscription-signin

Or, if you publish/consume it as a package, install by name and add it to your profile's bundle list:

// $DSH_PROFILE_DIR/package.json
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-subscription-signin"] } }

Then restart DSH once. This matters and is explained under Troubleshooting — the client half is composed into the browser's boot graph at startup, so a freshly installed bundle has no page until the process restarts.

Use

From the GUI: Settings → Subscriptions, pick an account, press Sign in.

For an agent, or a headless harness, the same implementation is exposed as a tool with actions status, start, poll, cancel, signout, submitCode and diagnostics. Read status first; start returns the URL and, for device grants, the code you enter on the vendor's page.

Safety

This section is the reason to trust the plugin, so it is specific rather than reassuring.

It never sees a secret. No password, token, API key or refresh token is read, written, logged, or forwarded. The token is produced and stored by the Harness credential seam (ctx.authorization / ctx.credentials), not by this code. Audit it yourself — it is two files:

  • host.js — zero imports. No filesystem access, no network requests, no child processes. It asks the credential store whether a record exists, and can delete one on explicit signout with confirm: true; it never reads a token value. The one process.env write is the documented per-account env field (used so a provider that selects its endpoint from the environment can be configured per account), and it is restored when the attempt settles.
  • client.js — one dependency, react, supplied by the page. Its only fetch calls go to the plugin's own /plugins/subscription-signin route on your local Harness. Nothing is rendered from a credential.

The one network-bearing route is trust-gated. The panel reaches the Host half over an HTTP route that inherits the Harness's own checks (connection.requestRejection: Host/Origin validation and browser authentication). On top of that, every mutating call must be a JSON POST, which forces a CORS preflight and blocks form-based CSRF; bodies are capped at 64 KiB; responses are no-store + nosniff.

Not sanctioned by the vendors — read this. For ChatGPT-plan usage, the Harness targets the ChatGPT/Codex backend (chatgpt.com/backend-api/codex), which OpenAI's developer documentation tells third-party apps not to target for plan usage. This plugin surfaces behaviour the Harness already ships; it does not introduce it and cannot change it. The sanctioned path for third-party apps is Sign in with ChatGPT, which the Harness does not implement. Using a subscription outside a vendor's intended client may conflict with that vendor's terms, and vendors may rate-limit or block such traffic. You are choosing to do that with your own account. Do not use this to resell access, and do not use it to reach credentials that are not yours.

No credentials are in this repository. If you ever find one here, that is a bug — please open an issue. The repository was scanned for tokens, private keys, home paths and email addresses before its first commit. docs/FINDINGS.md does cite exact package versions and source symbols from the Harness, which is deliberate: a claim about a specific build is only useful if it names the build.

Found a vulnerability? Report it privately via SECURITY.md rather than a public issue. That file also lists what is and is not in scope — worth reading first, because the unofficial-backend caveat above is a documented trade-off rather than a defect, and the two are easy to confuse.

Configuration

Accounts are configured, not hardcoded. They live in this package's own cordis.patch.yml and a profile may override that row.

FieldMeaning
accounts[].keyCredential key the seam already has a flow for, e.g. llm-pi-ai/anthropic
accounts[].labelDisplay name
accounts[].loginMethodPin a method when a provider offers several (oauth, device_code, browser, api-key)
accounts[].envEnvironment overrides for the flow, e.g. Kimi's regional OAuth host
accounts[].textAnswerAnswer to a blocking free-text question the flow raises (blank is often the right answer)
timeoutSecondsAttempt timeout, default 900
allowSignOutSet false to remove the sign-out control entirely

Adding a provider is one row — as long as the Harness already registers a flow for its credential key. List them with the tool's status action or in the panel; a key with no flow reports No sign-in flow registered rather than failing silently.

Kimi For Coding is the subtle one. Kimi Code is OAuth — an RFC 8628 device grant — but its login page is regional: auth.kimi.com is the mainland service (phone or WeChat) and points international users at .ai. The host is env-selectable, so the account names it:

- key: llm-pi-ai/kimi-coding
  env:
    KIMI_CODE_OAUTH_HOST: https://auth.kimi.ai

The .ai host is verified to answer the device-authorization endpoint and to serve the overseas login page (Google, email, international phone, SSO) — not the mainland page. The token exchange itself is unverified.

Troubleshooting

The Subscriptions page is missing from Settings after installing. Restart DSH. This is not a bug you can fix by re-installing, and it is worth knowing why: dsh-client-modules memoizes a package's dsh.client declaration per loader source key, including the negative answer, "until restart". If the bundle was ever resolved before its manifest declared dsh.client, that null is cached for the life of the process, the bundle never enters the boot graph, and the section cannot render or report an error — it simply does not exist. A re-install reuses the same loader name and consults the same poisoned key. Only a restart clears it. The diagnostics action reports bundleInGraph, composedPackages and clientPath, which is how to tell a missing bundle from a throwing component.

A provider says "No sign-in flow registered". The Harness LLM plugin that registers that flow is not mounted, or the credential key is misspelled. Keys are llm-pi-ai/<provider>.

A model from a pinned route fails when selected. Pinning models: bounds a route; it does not check your subscription. On a catalog route the route's own list is authoritative, so a model your plan does not include is still offered and fails only when requested. Unpinned routes self-correct, because the vendor enforces entitlements per request.

An attempt blocks another. One attempt per credential key at a time, mirroring the seam. Cancel before switching accounts.

Tests

Five dependency-free suites drive both halves against stubbed Harness services — no network, no credential store, no real installation touched. Run them from the repository root:

node smoketest/render.mjs     # renders every UI branch in Node
node smoketest/accounts.mjs   # the shipped account list + multi-account paths
node smoketest/run.mjs        # tool and shared-service paths
node smoketest/http.mjs       # the route: 15 assertions incl. the trust gate
node smoketest/bundles.mjs    # is the client half actually in the boot graph?

render exists because a component that throws during render blanks its whole slot silently — this is the only cheap way to see that failure. bundles exists because a missing client bundle is equally silent, and the two look identical from the user's side. Every suite imports the package's real modules directly, so no suite can pass against a stale copy.

CI runs all five on Node 20, 22 and 24 for every push and pull request, plus a check that the manifest still declares a web client half whose export resolves to a real file — the failure mode described in docs/FINDINGS.md. Note what the workflow does not do: no npm install. The package has zero dependencies and the tests import nothing outside the standard library. If that ever stops being true, CI fails, which is the signal we want.

Contributing

  • Found a security problem? Please report it privately — see SECURITY.md. That file also states what is in scope, which matters here: the unofficial-backend caveat below is a deliberate documented trade-off, not a vulnerability.
  • A page is missing, or a sign-in fails? Open a bug report. Please include the diagnostics output (it contains no secrets) and which of the two silent failure modes you are seeing — a missing page and a throwing page look identical but have different causes.
  • A provider's flow is wrong or absent? That is upstream in the Harness, not this plugin — it implements no OAuth. A plugin-side workaround may still be possible, so do open an issue.
  • This project follows the Contributor Covenant.
  • See CONTRIBUTING.md for the house rules — no new dependencies, never read a secret, keep the trust gate.

Support

This plugin is free and stays MIT. There is no paid tier, no gated feature, and nothing here is withheld behind a sponsorship — sponsorship funds maintenance, not access.

If it is useful to you and you would like to say so financially, GitHub Sponsors is the way. It is entirely optional, and it does not buy priority or influence over the roadmap.

Non-financial support is genuinely as useful, and often more so:

  • a bug report with the diagnostics output attached;
  • confirming that a route works, or does not, on a provider you pay for — the six unverified routes in the status table above are the biggest gap in this project, and one report closes part of it;
  • telling someone the plugin exists.

License

MIT — see LICENSE.

Not affiliated with, endorsed by, or sponsored by DeepSeek, OpenAI, Anthropic, GitHub, xAI, Meta, Moonshot or any other vendor named here. Product names are used only to identify the subscriptions being connected.