feat: monorepo CLI (workspace dev runner, app commands, platform-module auth)#35
Conversation
…ule auth Rebased the monorepo-cli snapshot onto main, layering its monorepo support on top of main's PR #31-#34 hardening (tunnel resilience, internal-secret, on-401 refresh) rather than reverting it. New: - `mirrorstack dev` go.work workspace runner: outer mode (docker compose up --build) + inner mode (--all: spawn go run per module). Replaces the single-module compose bootstrap + process runner. - `mirrorstack apps create` subcommand (api::create_app/App/CreateAppInput). - `mirrorstack module register`: scan go.work, create unregistered modules, write platform IDs back into main.go (module_meta read/write). - Per-module `.ms-platform-token-<slug>` files carrying each tunnel's dispatch-minted service token (docs/platform-module-auth.md). Preserved from main: - credentials with_refresh_retry / refresh_and_save / ACCESS_TTL and the login-hint expiry refresh (the snapshot had reverted these). - tunnel.rs typed RegisterError + RegisterResult + internal_secret on the register frame; service_token additively threaded onto TunnelHandle. - api.rs refresh_session/RefreshResponse signature (dropped the snapshot's duplicate TokenPair refresh). - on-401 with_refresh_retry on every authenticated call, including the new apps/create and module/register paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pure rustfmt formatting — no logic change. Clears the failing `cargo fmt --all -- --check` step so the Lint job (and the clippy step gated behind it) runs green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge-readiness analysis — finalizedRan a 4-dimension analysis (auth dual-path, #31–34 hardening preservation, CI/mergeability, blocker scan) + synthesis. The only blocker was the red Lint check — now fixed (pushed PASS — auth hardening preserved: PASS — dual platform→module auth is safe (Caveat #1 → keep both): the SDK resolves exactly one secret in strict priority ( Caveat #2 (live-verification) → partially cleared: a live run this session parsed a real Caveat #3 ( Non-blocking follow-ups: (1) Dev help text claims a single-module fallback that doesn't exist ( Verdict: merge once CI is green (build×3 + tests already pass; the fmt push clears Lint→clippy). |
Summary
Rebases the long-lived
feat/monorepo-clisnapshot (a single pre-#31 wip commit) onto currentmain, layering its monorepo support on top of the PR #31–#34 hardening that landed while it was diverged — rather than reverting that hardening (which the raw snapshot did).New capabilities
mirrorstack devworkspace runner (src/commands/dev/workspace.rs, rewrittendev/mod.rs): parsesgo.workand runs every module. Two modes:docker compose up --buildagainst a rootdocker-compose.yml.--all, inside the runner container): spawnsgo runper module directly, line-prefixed, with a shared Ctrl-C / try_wait loop.compose.rs(bootstrapped template) andprocess.rs(singlego run .), both deleted.mirrorstack apps create(src/commands/app/mod.rs+api::create_app/App/CreateAppInput).mirrorstack module register(module/mod.rs): scansgo.work, creates unregistered modules, and writes the platform-assigned ID back into eachmain.go(module_metagainedread_module_meta/write_module_id).docs/platform-module-auth.md): each tunnel's dispatch-mintedstk_*service token is written to.ms-platform-token-<slug>so every spawned module authenticates platform-initiated calls against ITS own session (fixes the old single-shared-file 401 bug).Rebase Notes — conflict resolutions
The snapshot independently rewrote the exact regions
mainhardened, so these were semantic merges, not mechanical:src/credentials/mod.rswith_refresh_retry/refresh_and_save/ACCESS_TTLand hardcoded the API URLsrc/api.rsrefresh_sessionreturningTokenPairrefresh_session/RefreshResponse; additively layered the snapshot'sApp/CreateAppInput/create_app+ its 2 tests. DroppedTokenPair.src/commands/dev/tunnel.rsservice_tokento the handleRegisterError/RegisterResult/internal_secret/typedrpc.err) and additively threadedservice_tokenontoTunnelHandle(union).src/commands/dev/mod.rsgo.workarchitecture and ported main's hardening into it:with_refresh_retryon the tunnel-token mint,internal_secreton everyRegisterPayload, the typedRegisterErrorUX (dev-mode-off browser deep-link, ModuleNotYours, Rejected) per module, andmint_internal_secret/dev_mode_off_hint/dev_console_urlhelpers.src/commands/dev/process.rsworkspace.rs+ the inner-mode spawn loop supersede it.src/commands/dev/compose.rsdocker-compose.yml.Restored main's #32/#33/#34 hardening (checklist)
credentials/mod.rstaken from main (with_refresh_retry + refresh_and_save + ACCESS_TTL + login-hint refresh).tunnel.rskeeps main's typedRegisterError/internal_secret;service_tokenadded without downgrading to anyhow.api.rskeeps main'srefresh_session/RefreshResponse; FORBIDDEN→Unauthenticated preserved.with_refresh_retryre-applied on all authenticated calls the snapshot had downgraded to barecreds.access_token— including the newapps createandmodule registerpaths.cargo build,cargo test(77 passed, incl.with_refresh_retry_*+credentials_lifecyclefrom main's tests), andcargo clippy --no-depsall green.MS_INTERNAL_SECRETinjection" as the enforce-auth mechanism. But the snapshot's committeddocs/platform-module-auth.mddeliberately replaces that with the per-tunnelstk_*service token (per-module.ms-platform-token-<slug>files), citing that a CLI-minted random secret can't match the platform's. I kept both: the per-module platform-token files (the doc's primary path) and a per-sessionMS_INTERNAL_SECRETthat is sent on the register frame and injected into the compose env (the SDK's documented backward-compat fallback, and required because main'sRegisterPayload.internal_secretfield must be populated to compile). Confirm this dual-path is what you want, or whetherMS_INTERNAL_SECRETshould be dropped entirely once the SDK no longer needs the fallback.mirrorstack devwas rearchitected single-module → workspace.DevArgslost--no-compose/--db-url; inner mode now readsDATABASE_URL. The dev/tunnel/dev-mode UX has not been manually live-verified (tunnel login + Google client were noted as expired in prior sessions). Please smoke-testmirrorstack dev(outer +--all) and--tunnelagainst a real go.work workspace.--watchflag is declared but not wired (placeholder from the snapshot).Superseded branches (left in place, not deleted):
feat/5-loginandfeat/scaffold-v0.2are both fully superseded by main's content.🤖 Generated with Claude Code