Skip to content

feat: monorepo CLI (workspace dev runner, app commands, platform-module auth)#35

Merged
I-am-nothing merged 2 commits into
mainfrom
chore/monorepo-cli-rebase
Jun 15, 2026
Merged

feat: monorepo CLI (workspace dev runner, app commands, platform-module auth)#35
I-am-nothing merged 2 commits into
mainfrom
chore/monorepo-cli-rebase

Conversation

@I-am-nothing

Copy link
Copy Markdown
Contributor

Summary

Rebases the long-lived feat/monorepo-cli snapshot (a single pre-#31 wip commit) onto current main, 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 dev workspace runner (src/commands/dev/workspace.rs, rewritten dev/mod.rs): parses go.work and runs every module. Two modes:
    • Outer (host, default): docker compose up --build against a root docker-compose.yml.
    • Inner (--all, inside the runner container): spawns go run per module directly, line-prefixed, with a shared Ctrl-C / try_wait loop.
    • This supersedes the single-module compose.rs (bootstrapped template) and process.rs (single go run .), both deleted.
  • mirrorstack apps create (src/commands/app/mod.rs + api::create_app/App/CreateAppInput).
  • mirrorstack module register (module/mod.rs): scans go.work, creates unregistered modules, and writes the platform-assigned ID back into each main.go (module_meta gained read_module_meta/write_module_id).
  • Per-module platform-token files (docs/platform-module-auth.md): each tunnel's dispatch-minted stk_* 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 main hardened, so these were semantic merges, not mechanical:

File Conflict Resolution
src/credentials/mod.rs snapshot reverted with_refresh_retry/refresh_and_save/ACCESS_TTL and hardcoded the API URL Took main wholesale. Snapshot version was a strict regression.
src/api.rs snapshot added a duplicate refresh_session returning TokenPair Kept main's refresh_session/RefreshResponse; additively layered the snapshot's App/CreateAppInput/create_app + its 2 tests. Dropped TokenPair.
src/commands/dev/tunnel.rs snapshot reverted typed errors back to anyhow; added service_token to the handle Took main's error-typing (RegisterError/RegisterResult/internal_secret/typed rpc.err) and additively threaded service_token onto TunnelHandle (union).
src/commands/dev/mod.rs dominant conflict — two divergent architectures Kept the snapshot's outer/inner go.work architecture and ported main's hardening into it: with_refresh_retry on the tunnel-token mint, internal_secret on every RegisterPayload, the typed RegisterError UX (dev-mode-off browser deep-link, ModuleNotYours, Rejected) per module, and mint_internal_secret/dev_mode_off_hint/dev_console_url helpers.
src/commands/dev/process.rs modify/delete (main modified, snapshot deleted) Accepted the deleteworkspace.rs + the inner-mode spawn loop supersede it.
src/commands/dev/compose.rs clean delete (main never touched) Accepted the delete — outer mode uses a root docker-compose.yml.

Restored main's #32/#33/#34 hardening (checklist)

  • credentials/mod.rs taken from main (with_refresh_retry + refresh_and_save + ACCESS_TTL + login-hint refresh).
  • tunnel.rs keeps main's typed RegisterError/internal_secret; service_token added without downgrading to anyhow.
  • api.rs keeps main's refresh_session/RefreshResponse; FORBIDDEN→Unauthenticated preserved.
  • with_refresh_retry re-applied on all authenticated calls the snapshot had downgraded to bare creds.access_token — including the new apps create and module register paths.
  • cargo build, cargo test (77 passed, incl. with_refresh_retry_* + credentials_lifecycle from main's tests), and cargo clippy --no-deps all green.

⚠️ Why DRAFT — reviewer must verify

  1. Auth-mechanism reconciliation deviates from the rebase recon. The recon said to "port main's MS_INTERNAL_SECRET injection" as the enforce-auth mechanism. But the snapshot's committed docs/platform-module-auth.md deliberately replaces that with the per-tunnel stk_* 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-session MS_INTERNAL_SECRET that is sent on the register frame and injected into the compose env (the SDK's documented backward-compat fallback, and required because main's RegisterPayload.internal_secret field must be populated to compile). Confirm this dual-path is what you want, or whether MS_INTERNAL_SECRET should be dropped entirely once the SDK no longer needs the fallback.
  2. mirrorstack dev was rearchitected single-module → workspace. DevArgs lost --no-compose/--db-url; inner mode now reads DATABASE_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-test mirrorstack dev (outer + --all) and --tunnel against a real go.work workspace.
  3. --watch flag is declared but not wired (placeholder from the snapshot).

Superseded branches (left in place, not deleted): feat/5-login and feat/scaffold-v0.2 are both fully superseded by main's content.

🤖 Generated with Claude Code

…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>
@I-am-nothing I-am-nothing marked this pull request as ready for review June 15, 2026 16:29
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>
@I-am-nothing

Copy link
Copy Markdown
Contributor Author

Merge-readiness analysis — finalized

Ran 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 cargo fmt --all, no logic change). Once CI re-runs green this is merge-ready.

PASS — auth hardening preserved: credentials/mod.rs is byte-identical to main; tunnel.rs/api.rs keep the typed RegisterError machinery + FORBIDDEN→Unauthenticated mapping; every authenticated call (apps-create, module-register, dev tunnel-token mint, whoami) is wrapped in with_refresh_retry. No bare-token calls.

PASS — dual platform→module auth is safe (Caveat #1 → keep both): the SDK resolves exactly one secret in strict priority (MS_PLATFORM_TOKEN_FILE > MS_PLATFORM_TOKEN > MS_INTERNAL_SECRET), never additively trusting both. The per-module .ms-platform-token-<slug> files are the enforced primary; MS_INTERNAL_SECRET is a correctly-subordinated fallback the SDK still consumes (middleware.go:383-385) and RegisterPayload.internal_secret still requires. Dropping it now would be premature → defer, SDK-first.

Caveat #2 (live-verification) → partially cleared: a live run this session parsed a real go.work (3 modules), opened per-module WSS tunnels, wrote .ms-platform-token-<slug> files, and served+provisioned the modules — exercising run_outer → open_tunnels → token-file write. Not yet covered: dev --all (inner mode) and dev --tunnel without --local-url (default URL construction). Post-merge smoke tests.

Caveat #3 (--watch unwired) → acceptable placeholder (clippy-clean; silent no-op).

Non-blocking follow-ups: (1) Dev help text claims a single-module fallback that doesn't exist (dev::run() hard-errors without go.work); (2) templates/dev/docker-compose.yml.tmpl is now orphaned (delete); (3) --watch should emit a 'not wired' notice or be hidden; (4) de-dup the verbatim-copied go.work parser (workspace.rs vs module/mod.rs); (5) deprecation ticket to drop MS_INTERNAL_SECRET from the CLI dev path once the SDK fallback is removed (SDK-first).

Verdict: merge once CI is green (build×3 + tests already pass; the fmt push clears Lint→clippy).

@I-am-nothing I-am-nothing merged commit 44e3a2a into main Jun 15, 2026
5 checks passed
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