The standard, proven procedure for keeping Claude Code and Codex subscriptions signed in — Google-account profiles, normal-browser sign-in, repair, verification, and when to hand off. Use whenever a subscription shows "needs sign-in", a repair fails, or you set up a new account on a machine.
Install
npx skillscat add rolandcanyon-cmd/home-consciousness/subscription-signin Install via the SkillsCat registry.
/subscription-signin
How an Instar agent keeps its Claude Code and Codex subscriptions signed in, using only what has been proven on real accounts. Follow it with judgment; do not replace it with a script.
API calls below use Authorization: Bearer $AUTH against http://localhost:$INSTAR_PORT (port is in .instar/config.json).
The chain (why it works)
- Each Google account has its own Chrome profile on each machine, kept signed in to Google. Claude and Codex both sign in "with Google", so a healthy Google session in that profile is what makes every later re-sign-in a few clicks.
When Google itself asks to sign in again, the agent types the account's password and its 6-digit authenticator code, both taken from the vault by name, in that same normal browser. No passkey and no automated browser are needed, so the Google side needs no human either. - Sign-ins always run in a NORMAL browser — the account's Chrome opened the ordinary way, never a remote-controlled/automated one (no DevTools/Playwright). Providers put human checks in front of automated browsers (Claude's Authorize never went through; Cloudflare "Just a moment" never cleared); the same profile opened normally passes. The built-in repair does this for you on macOS. (Another agent has driven Google's own sign-in and ChatGPT's "Continue with Google" through a debugging-protocol Chrome without trouble. The page that reliably refuses automation is Claude's Authorize page. A normal browser works for all of them, so use one everywhere.)
- The CLI login is started by Instar, the browser only approves it: Claude gives a code to paste back; Codex (device code) finishes on its own.
- Success is measured, not assumed: a real authenticated call must work and answer as the expected email. A status command alone is not proof:
claude auth statusreportsloggedIn: truefor a sign-in whose session has expired and can't be refreshed.
Hard rules
- Never use an automated browser for a sign-in page. Never solve or work around a CAPTCHA or phone check — hand it to the operator.
- Never pick an account other than the expected one; never approve permissions beyond what the CLI requested.
- Never put a password, code, or token in chat, a file, or a command line. Secrets come from the vault by name.
- Never copy a Chrome profile or a login between machines — cookies are tied to that machine. Each machine gets its own profile and its own sign-in.
- Never drive or close a Chrome window you did not open (a person may be using it).
- Type a password only into Google's or Claude's own sign-in page, after checking that page's address.
1. Setting up an account on a machine (once)
- Check the profile registry:
GET /playwright-profiles/resolve?service=google&identity=<email>. If there is none, create it:POST /playwright-profilesthenPOST /playwright-profiles/<id>/accountswith{"service":"google","identity":"<email>","owner":"operator"|"agent","vaultRefs":[...]}. - Make sure the vault holds the account's Google password and its authenticator (TOTP) secret, e.g.
google_password_<name>/google_totp_<name>, and that the profile's account entry lists them invaultRefs. If the account's authenticator is already on the person's phone (Google allows only one), don't replace it: store the account's unused backup codes instead (google_backup_codes_<name>, bound asbackupCode). With both present the agent can sign the profile in to Google itself. If the account has no authenticator yet, adding one changes the person's 2-step settings: ask once for a yes, then add it from the signed-in profile and store the secret. - Enroll the subscription if it is not in the pool:
POST /subscription-pool/enroll(never ask anyone to paste a token).
2. When a subscription needs sign-in
- Look first:
GET /subscription-pool(which account, which machine,needs-reauth),GET /subscription-relogin(any repair episode and its state),GET /subscription-pool/pending-logins(a live login waiting for approval). - Let the built-in repair run. In unattended mode it starts on its own for the listed identities; in approval mode it needs one dashboard tap (Repair sign-in on that account × machine cell). You cannot approve for the operator — send them the dashboard link.
- Read the outcome:
GET /subscription-relogin/<episode>/events(redacted).succeeded= done. Otherwise use the table in section 4. - If the repair stopped on a page rule rather than on a real human check (for example
unexpected-origin,permission-expansion, a timeout, or a page it didn't recognize), sign in by hand (section 3). Don't wait for a retry. The by-hand path is what a person at the machine would do, and it has worked on every machine. - Verify:
GET /subscription-poolshows the accountactive,identityDrifted: false, right email.
3. Signing in by hand (the agent at the machine)
This is the way you would do it as a person: look at the screen and act on what it needs. It works where fixed page rules stop. Proven on the Mac Studio (2026-09-24) and the Laptop (2026-09-25, two accounts in about 10 minutes). The only human step was one phone tap.
- Start the CLI login for the pool account:
POST /subscription-pool/enrollwith the account's existingid,label,provider,frameworkandconfigHome. It returns theverificationUrl. Cancel a stale pending login first. - Open the account's own profile normally: set
browser.allow_javascript_apple_events: truein<profile>/Default/Preferences, thenopen -na "Google Chrome" --args --user-data-dir=<profile dir> "<verificationUrl>". Its main process is the one whose arguments carry that--user-data-dirand no--type=. - See the screen:
screencapture -x a.png b.png c.png(one file per display; a full-screen app on one display hides the others), then view the images. Read the page itself with a pid-targeted Apple Event (CrSu/ExJa, parameterJvSc, sent totab 1 of window 1, viaosascript -l JavaScript). Reading the "active tab" property fails. The window index follows stacking order, so a Google popup becomes window 1 while it is open. - Act like a person:
- A "Sign in to Chrome" window appears first on a fresh profile. While it is up, the browser reports no windows, so nothing else works. Click Stay signed out. After a Google sign-in, Chrome may also offer "Set up a work profile" or "Make Chrome your own". Choose Use Chrome without an account every time.
- A new window can open inside another app's full-screen Space, where it is invisible. Move it onto a free display (a pid-targeted Apple Event that sets window 1's bounds) before clicking.
- Before every keystroke, check that the frontmost process is this Chrome's pid AND that
document.activeElementis the field you mean to type into. Keystrokes go to whatever app has focus, and once they landed in the operator's chat box. If that ever happens, delete exactly what you typed and never press Enter. - Click with
cliclickat screen coordinates you compute from the page:screenX + rect.left + width/2andscreenY + (outerHeight - innerHeight) + rect.top + height/2. Negative coordinates need the=form (c:=-2192,=250). Move the window onto a free display first if needed. - Type a password by piping it from the vault into a script that reads stdin and sends System Events keystrokes, after checking that this Chrome is frontmost. The secret must never appear on a command line.
- Claude's Authorize button enables only after the window has focus and sees pointer movement, and even then it can take several seconds. Raise the window, click a blank spot, and move the pointer onto the button in a few steps. Then wait up to about 15 seconds for it to enable, and click once. Never click it while it is still greyed out.
- If Google offers a passkey first (a Touch ID sheet or Chrome's passkey picker), cancel it, then choose Try another way, then Enter your password, then the authenticator-app code. Compute the code from the vault's TOTP secret over stdin, and never print it.
- Recompute an element's coordinates right before clicking it. A page that scrolled makes old coordinates land on the wrong thing.
- The usual Claude path: the authorize link opens
claude.ai/login, then Continue with Google opens a Google popup (choose the expected email, then the password, then any second step). Next comes "You're signing back in to Claude" → Continue → the authorize page (check it says "Logged in as" the expected email) → Authorize → a page showing the code. Read the code from the page and pipe it straight intoPOST /subscription-pool/follow-me/enroll/<id>/submit-code, never printing it. - Verify (
POST /subscription-pool/poll, thenGET /subscription-pool, plus one real call:CLAUDE_CONFIG_DIR=<configHome> claude -p "Reply with exactly: OK" --max-turns 1, run next to a known-good account so that a failure means something). Then close only the Chrome you opened.
Codex (device code), proven on the Laptop (2026-09-25; three accounts in about 15 minutes):
- The login usually already exists (
GET /subscription-pool/pending-logins, reissued every few minutes). Don't enroll it again, because that can respawn it with a new code. - Read the code from the live login pane, not only from pending-logins:
tmux capture-pane -p -t '=instar-enroll-codex-cli-<tail of configHome>:'. If the two differ, trust the pane. OpenAI accepts an old code, but then the CLI never finishes. - Open
https://auth.openai.com/codex/devicein the account's profile, then Continue with Google, then any Google sign-in. The consent page shows the email: check it, then Continue. On the page that asks for the 9-character code, click the first box and type the code (the dash is skipped). Then Continue, and the page shows "Signed in to Codex". The CLI finishes on its own within about 10 seconds. - Then
POST /subscription-pool/enroll/<id>/complete; a device-code login is not marked complete by itself. ThenPOST /subscription-pool/poll. If the code expired mid-flow,POST /subscription-pool/enroll/reissue-expiredand type the new one. - Verify with
CODEX_HOME=<configHome> codex login status("Logged in using ChatGPT") and a fresh quota reading from Codex's own server (POST /subscription-pool/poll, sourcecodex-app-server).
If you are helping from another machine, spawn the helper session on the target machine bound to the operator's topic. An unbound session cannot message the operator. Tell the operator before anything that needs their hands, such as a phone tap or a macOS "Allow".
Agent-run repair (when Instar starts you as the sign-in helper)
On macOS the built-in repair can hand this procedure to one short-lived helper session (relogin-<episode>, navigation agent-session). If that is you:
- The CLI login is already started. Your prompt carries the verification URL (and the Codex device code), the expected email, the Chrome profile directory, the vault entry NAMES, and a per-episode token. Do steps 2–5 above. Do NOT enroll, cancel, reissue or complete any login, and do not call
submit-codeorenroll/<id>/complete: Instar does those itself. - Claude: read the code from the page into a shell variable (never print it) and post it as
{"code":"…"}tohttp://127.0.0.1:<port>/subscription-relogin/<episode>/codewith the headerX-Relogin-Helper-Token: <token>andContent-Type: application/json, piping the JSON on stdin. A202means Instar has it. Then close only the Chrome you opened and exit. - Codex: type the device code on the page and continue to "Signed in to Codex". Post nothing; Instar sees the credential land.
- A phone "Is it you?" tap: post
{"notify":"phone-tap"}to the same route once, then wait a few minutes for the page to move on. - A macOS Allow prompt (Screen Recording, Accessibility, or Automation): post
{"notify":"macos-permission","permission":"screen-recording"}(oraccessibility/automation) and exit. - At a hard line, or when you cannot finish, write the reason in your final output and exit. You have at most 15 minutes. You cannot message anyone.
- Instar decides success, never you: the login must complete, the email must match, and a real authenticated call must work.
A machine with no usable browser (sign it in from a Mac)
Proven 2026-09-27: a Windows machine running Instar inside WSL went from 6 signed-out and 5 missing accounts to all 13 (8 Claude, 5 Codex) signed in and verified. The approving browser was on a Mac. The Mac's screen was locked the whole time.
Use this when the machine that needs a login (the target) cannot run a normal Chrome you can drive (Windows/WSL, headless Linux, or no Google profiles), and a Mac you run on holds the account's Google profile (section 1).
- The target starts its own login. Call its
POST /subscription-pool/enrollover the mesh (Bearer token; peer URL from.instar/machines/registry.json). The login is minted there, so nothing is copied between machines. If the account is new on the target, give it aconfigHomeon the target (for example/home/<user>/.claude-followme-<id>). - The Mac approves it in the account's Chrome, with no screen needed. Launch it with
open -naand drive the page only with pid-targeted Apple Events JavaScript (section 3 step 3). Click with the page's ownelement.click(), notcliclick. This needs no Screen Recording or Accessibility permission, so a locked screen is not a blocker. Before calling anything blocked, re-run this method at least once. A failed screenshot proves nothing here. - Before the first launch, set the profile's Preferences:
browser.allow_javascript_apple_events: true, plus pop-up exceptions (profile.content_settings.exceptions.popups,setting: 1) forhttps://claude.ai:443,*,https://claude.com:443,*,https://auth.openai.com:443,*,https://chatgpt.com:443,*andhttps://accounts.google.com:443,*. The first launch after editing them often exposes no scriptable window (Apple Event error-1719). Close that Chrome and launch again. - Claude: Continue with Google → pick the element whose
data-identifieris the expected email → Continue → on the Authorize page, check "Logged in as" the expected email and the standard permission list. Enable Authorize without a mouse: dispatchfocuson the window andpointerdown/pointerup/pointermove/mouseoveron the body and the button, then re-check every 3 s. Click it, read the code from the callback page, and pipe it (never printed) into the target'sPOST /subscription-pool/follow-me/enroll/<id>/submit-code. On this path the replyoutcome: "held", reason: "missing-expected-email", got: <email>means the login completed as that email; only the automatic pool-add was held. - Codex: open
https://auth.openai.com/codex/device→ Continue with Google → consent (check the email) → workspace choice if shown (see the table) → fill inputscharacter_1…character_9with the target'suserCodeminus the dash (native value setter +input/changeevents) → Continue → "Signed in to Codex". Then call the target'sPOST /subscription-pool/enroll/<id>/complete. - Register accounts new to the target:
POST {target}/subscription-poolwith{id, nickname, provider, framework, configHome, email}. The target reads the email from the fresh credential. - Verify on the target, and against the whole pool: poll, then check each account shows
loginCheck: "ok"(Codex:lastQuota.source: "codex-app-server"). Run one realclaude -pper Claude account in a session spawned on the target. Compare the target's list with the dashboard's "Accounts on each machine" grid (GET /subscription-pool?scope=pool): a machine that was offline while accounts were added elsewhere never has them.
4. When a repair does not finish
| What the episode/page shows | What it means | Do this |
|---|---|---|
captcha / phone-confirmation / operator-only |
Provider wants a human | Tell the operator once, with the dashboard link; never retry through it |
unexpected-origin / permission-expansion right after Chrome opens |
Usually a false stop: a "Sign in to Chrome" window, a Google popup, or the standard Claude Code permission list | Sign in by hand (section 3). Check the permissions on the Authorize page yourself: they must be the standard Claude Code set, and nothing more |
| A "Sign in to Chrome" window in front | Chrome's first-run prompt for that profile | Click Stay signed out, then carry on |
relogin-profile-in-use |
That profile's Chrome is already open | Wait until it is closed; never close it yourself |
pending-login-already-live on retry |
An older login is still waiting | POST /subscription-pool/enroll/<id>/cancel, then retry |
| Google asks for the password or 2-step again | The profile's Google session expired | The repair types the password and authenticator code from the vault; if either is missing, do step 1.2 |
| Google asks for a phone tap, SMS, or "verify it's you" | Google's own risk check | Hand to the operator once; never try another method to get around it |
| Google asks for a second step on an account whose authenticator is on the person's phone | Normal for those accounts | The repair uses ONE saved backup code (vault entry bound as backupCode), removed from the list before use; when the list runs low, make new codes from the account's security page |
Operator-only with reason plain-browser-automation-not-permitted |
macOS hasn't allowed the agent to control Chrome on this machine | Ask the operator once to allow it (System Settings, Privacy & Security, Automation), then Try repair again |
| "Continue with Google" returns "There was an error logging you in" | The profile blocks Google's sign-in pop-up | Allow pop-ups for the Claude, OpenAI and Google sign-in origins in the profile's Preferences (see "A machine with no usable browser"), relaunch, retry |
| Codex consent shows "Select a workspace" and Continue does nothing | The pre-selected workspace forbids device-code sign-in ("contact your workspace admin") | Pick the workspace the account normally uses (match the chatgpt_account_id of a known-good copy of that account), then Continue |
| A machine shows "Set up" for accounts the other machines have | It was offline when those accounts were added; its pool never had them | Compare against the pool-wide list, not the machine's own; sign in and register the missing ones there |
| Anything else | Read the reason | GET /subscription-relogin/EPISODE/events: every attempt records a short reason token (e.g. chrome-launch-timeout) |
| Profile missing on this machine | Never set up here | Do section 1 on this machine |
| Authorize button stays greyed out | Normal on Claude until the page sees pointer activity | The repair handles it; if it persists after a minute, hand off |
| Repeated failures on one account | Something structural | Stop the automatic retries after two; sign in by hand once (section 3); if that also fails, report the episode id and what you saw |
5. Keeping it healthy (the 20% that prevents 80% of failures)
- One profile per Google account per machine, registered, signed in to Google, with password and authenticator secret in the vault.
- Pool
activedoes not mean signed in, and neither does a status command. Prove it with one real authenticated call per account, run next to a known-good account, and check which email it answers as. On 2026-09-25, three Codex accounts readactivefor days while they were signed out. On another machine,claude auth statussaid signed in for three accounts whose sessions were dead. - A check only counts once you've seen it fail. Try it on a signed-out or bogus account at least once, so you know it can tell the difference.
- Keep each profile's Google session in use: open it in a normal browser about weekly, so an expiry is caught before Claude or Codex needs it.
- Don't hammer sign-in pages: each failed automated-looking attempt raises the provider's risk score. One clean attempt, then hand off.
- When you learn something new about a sign-in page, update this skill's table — the procedure is the memory.