Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ced8d07
feat!: simplify hack to a local-first core
roodboi Apr 20, 2026
b310f6d
chore: merge main into simplify-hack-platform
roodboi Apr 20, 2026
bbe8f69
fix: address dispatch and tickets review feedback
roodboi Apr 20, 2026
11f39e5
fix: address ci and lifecycle review feedback
roodboi Apr 21, 2026
97a1151
fix: serialize tickets worktree access
roodboi Apr 21, 2026
d8a718e
feat: publish portable runtime images
roodboi Apr 21, 2026
172b975
test: smoke portable managed containers
roodboi Apr 21, 2026
709b582
fix: read shared env keys from primary worktrees
roodboi Apr 21, 2026
0fd6dc1
fix: close env and tickets review regressions
roodboi Apr 21, 2026
d767231
fix: harden lifecycle cleanup and restore CI
roodboi Apr 21, 2026
08c12ad
fix: restore lifecycle and env compatibility cleanup
roodboi Apr 21, 2026
147adb6
fix: suppress inactive gateway token doctor noise
roodboi Apr 22, 2026
95bd545
feat(lifecycle): support singleton host process adoption
roodboi Apr 22, 2026
196bd90
docs(lifecycle): explain singleton adoption semantics
roodboi Apr 22, 2026
748a072
fix(lifecycle): avoid external singleton port probes
roodboi Apr 23, 2026
87d2041
docs(agent): refresh local-first setup guidance
roodboi Apr 23, 2026
291cca1
fix(runtime): harden env ignores and singleton probes
roodboi Apr 23, 2026
5ad7259
fix(env): tolerate missing git and preserve ssh wrappers
roodboi Apr 23, 2026
e52af68
fix(env): preserve legacy local overlays without git
roodboi Apr 23, 2026
6d2e9d8
fix(runtime): close remaining review regressions
roodboi Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 23 additions & 8 deletions .codex/skills/hack-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ description: >

# hack CLI

Use `hack` as the primary interface for local development.
Use `hack` as the primary interface for local-first development.

## Product Boundary

- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, daemon, and optional local tickets.
- Removed surfaces: hosted auth/account/org/team flows, web dashboard, built-in GitHub workflows, and built-in Linear sync.
- Remote/gateway/node/dispatch code is unsupported experimental. Do not put it on the critical path for local dev unless explicitly requested.

## Operating Rules

Expand Down Expand Up @@ -44,6 +50,7 @@ Use `hack` as the primary interface for local development.
## Managed Files

- Source-of-truth files: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.default.yaml`, and optional `.hack/hack.env.<overlay>.yaml`.
- Worktree-local override files: `.hack/hack.env.local.yaml` and `.hack/hack.env.<overlay>.local.yaml`.
- Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (gitignored; machine-specific state).
- Generated by hack: `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose.<branch>.override.yml`.
- Managed via CLI: `.hack/.internal/extra-hosts.json` using `hack internal extra-hosts ...` commands.
Expand Down Expand Up @@ -77,7 +84,9 @@ Use `hack` as the primary interface for local development.
- Start/stop/status: `hack global up`, `hack global down`, `hack global status`
- Global logs: `hack global logs <service> --no-follow --tail 200`

## Remote Nodes + Dispatch
## Unsupported Experimental Remote

These commands are source-available but outside the supported v3 product contract:

- Pair/register a node: `hack node pair ...`, then verify via `hack node list` and `hack node status --watch`.
- Repair SSH access for remote Git/mutagen: `hack node ssh setup --node <id>`.
Expand All @@ -89,17 +98,20 @@ Use `hack` as the primary interface for local development.

- Put host setup in `.hack/hack.config.json` under `startup` / `lifecycle`.
- Use lifecycle processes for long-running host tasks, not ad-hoc terminals.
- For fixed-port host helpers such as SSM tunnels or local proxies, set `singleton.ports` and usually `onConflict: "adopt"` so Hack reuses a healthy existing listener instead of starting duplicate tunnel stacks.
- `singleton` is a listener guard, not process ownership transfer; adopted external processes are left running on `hack down`.
- Inspect via `hack projects --details` and `hack logs <service-or-process>`.

## Verification Loops

- For `hack run` / `hack exec` / env-resolution changes, verify the effective env transition matrix in
`tests/project-run-command.test.ts`.
- Cover omitted env, explicit overlay, explicit `base`, default-overlay resolution, cached runtime-state env,
and target-service running/not-running.
- For lifecycle or startup-process changes, verify `tests/project-lifecycle-processes.test.ts`.
- Preserve `sh -c` semantics, process-group cleanup, stale pane-metadata reconciliation, and interactive stdin
behavior.
target-service running/not-running, worktree-local overrides, and host-vs-compose target mode.
- For lifecycle or startup-process changes, verify `tests/project-lifecycle-processes.test.ts` and
`tests/project-lifecycle-singleton.test.ts`.
- Preserve `sh -c` semantics, process-group cleanup, stale pane-metadata reconciliation, singleton listener
behavior, and interactive stdin behavior.
- When semantics change, update `docs/env.md` or `docs/lifecycle.md` in the same patch so future agent work starts
from the current contract.

Expand All @@ -124,8 +136,11 @@ Use branch instances to run parallel environments:

## Host-side env helpers

- One-off host command with injected env: `hack env exec --env qa --service api -- bun db:migrate`
- Interactive host shell with injected env: `hack env shell --env qa --service api`
- One-off host command with injected env: `hack host exec --env qa --scope api -- bun db:migrate`
- Host commands default to a host-local env view; use `--target compose` when you explicitly want container-oriented addresses.
- `--scope` selects which env scope to inject; it does not move execution into that service container.
- Interactive host shell with injected env: `hack host shell --env qa --scope api`
- Run inside an already-running service container: `hack exec api -- bun test`

## Tickets

Expand Down
7 changes: 0 additions & 7 deletions .factory/library/account-shell-hydration.md

This file was deleted.

53 changes: 25 additions & 28 deletions .factory/library/architecture.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,38 @@
# Architecture

Architectural decisions and patterns discovered during mission planning.

**What belongs here:** durable architecture rules, canonical paths, ownership boundaries, and shared design constraints.

---
Durable architecture rules for current Hack work.

## Core Product Boundary

- Hack remains CLI-first and local-first.
- `apps/web` is optional and must never become the only path for critical local workflows.
- Shared administration, integration management, and env/secret-sharing can prefer the web app, but CLI parity and local usability must remain intact.
- Hack v3 is CLI-first, local-first, and self-contained.
- Supported product surface: project init, local runtime orchestration, routing/TLS, env and secrets, lifecycle, sessions, diagnostics, MCP/agent setup, the slim macOS companion, and optional local tickets.
- Retired product surfaces: hosted auth, account/org/team admin, web dashboard, built-in GitHub workflows, and built-in Linear sync.
- Remote/gateway/node/dispatch code may remain source-available, but it is unsupported experimental and must stay out of first-run docs, release gates, and default agent paths.

## Auth Ownership
## Runtime Ownership

- Keep one coherent Better Auth model.
- The browser app owns the interactive web auth UX.
- `services/auth-broker` remains the auth/session and control-plane API backend.
- Do not introduce a second independent auth authority.
- `hack` is the source of truth for project start/stop/open/logs/session flows.
- `.hack/.internal/**` and `.hack/.branch/**` are generated runtime state and should not be hand-edited.
- Branch/worktree instances must clean up only their own runtime, lifecycle, and generated state.
- Doctor output should classify recovery as restartable, repairable, or configuration drift and give one concrete next command.

## Persistence Rules
## Env Ownership

- Shared org/team/project/integration state must be durable by default.
- In-memory-only admin state is mission work to remove or explicitly surface as dev-only if temporarily retained during transition.
- Use existing Neon + Drizzle foundations where practical instead of inventing a separate persistence layer.
- Canonical shared env files are `.hack/hack.env.default.yaml` and optional `.hack/hack.env.<overlay>.yaml`.
- Worktree-local overrides are `.hack/hack.env.local.yaml` and `.hack/hack.env.<overlay>.local.yaml`.
- `.hack/.env` and `.hack/.env.state.json` are derived compatibility artifacts, not runtime source of truth.
- Secret-key lookup order is checkout-local `.hack.secret.key`, git-common-dir shared key for linked worktrees, then `HACK_ENV_SECRET_KEY`.

## Linear Canonical State
## Lifecycle Ownership

- Repo-bound Linear artifacts belong under `.hack/linear/projects/<project-id>/...`.
- `.hack/.hack/linear/**` is a legacy bug surface and must not remain authoritative.
- Mission closeout scope is the frozen set of Hack-project Linear work open at mission start plus mission-created optional-web-control-plane work.
- Long-running host helpers belong in `lifecycle.processes` or `startup` entries with `persistent: true`.
- Fixed-port helpers such as AWS SSM tunnels should declare `singleton.ports`.
- Use `onConflict: "adopt"` only when a complete existing listener set is equivalent and should be reused.
- `singleton` adoption is listener-level reuse, not ownership transfer; Hack must leave adopted external processes running on `hack down`.
- Stale mux state should be recovered through lifecycle metadata carefully enough to avoid orphaning Hack-owned processes while not broadening cleanup to unrelated process groups.

## Durable Persistence Targets
## Tickets Ownership

- Better Auth-owned tables and broker-specific auth persistence currently live under `services/auth-broker/src/db/schema.ts`.
- Org/team admin state now persists in broker-owned `org_admin_*` tables under `services/auth-broker/src/db/schema.ts`, and the default auth-broker wiring uses the DB-backed store whenever `DATABASE_URL` is available.
- Auth-broker startup now reports the selected org/team store mode. When `DATABASE_URL` is absent, the broker runs in an explicitly surfaced development-only in-memory mode, and durable initialization failures must throw instead of silently falling back to memory.
- Broker-backed project registration and project-access visibility are now derived from the same org/team membership store (`services/auth-broker/src/modules/projects/service.ts` uses org/team visibility to decide which shared projects and grants are visible), so project-admin parity checks must account for active org/team scope rather than treating project ownership as independent state.
- Shared control-plane tables currently live under `packages/db/src/schema/core.ts`, with migrations/verification through `bun run db:generate`, `bun run db:migrate`, and `bun run db:push`.
- Workers may extract shared durable contracts, but they must keep one concrete Neon + Drizzle-backed persistence path and document any migration boundary changes in code/tests.
- Tickets are optional local helpers, not a headline hosted workflow.
- Durable ticket state is the git-backed JSONL journal under `refs/hack/tickets` or the configured branch ref.
- Local projection and checkout state under `.hack/tickets/` is rebuildable.
92 changes: 25 additions & 67 deletions .factory/library/environment.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,38 @@
# Environment

Environment variables, external dependencies, and setup notes.
Environment variables, external dependencies, and setup notes for current Hack work.

**What belongs here:** required env vars, external services, credential assumptions, platform/runtime quirks.
**What does NOT belong here:** service ports or lifecycle commands (use `.factory/services.yaml`).

---

## Credential Assumptions

- Existing local/remote credentials for Hack, GitHub, Linear, Railway, and Neon are assumed to be the source of truth for this mission.
- New env wiring may be added for `apps/web`, but live Vercel deployment is out of scope; the app only needs to be local + deploy-ready.
- Gateway writes are disabled by default and may be temporarily enabled only for explicit validation steps that require them.

## Auth-Broker Runtime Inputs

The broker already depends on environment such as:
- `DATABASE_URL`
- `BETTER_AUTH_SECRET`
- `GITHUB_CLIENT_ID`
- `GITHUB_CLIENT_SECRET`
- `HACK_PROVIDER_TOKEN_ENCRYPTION_KEY`
- any trusted-origin configuration needed for local Hack hosts and deploy-ready web origins

Workers should extend existing env handling rather than introducing parallel secret/config channels.

Repo-bound broker-auth verification can also use:
- `HACK_AUTH_SESSION_TOKEN`
- `HACK_AUTH_SESSION_EXPIRES_AT`

These let repo-bound CLI flows prove an authenticated broker session without reading stored local secret or keychain-backed auth state first. Use them when validating keychainless broker-seeded Linear flows.

## Web Auth Runtime Inputs

`apps/web/src/lib/auth-config.ts` currently derives the browser auth contract from:
- app base URL: `NEXT_PUBLIC_HACK_WEB_APP_BASE_URL`, `HACK_WEB_APP_BASE_URL`, `NEXT_PUBLIC_APP_BASE_URL`, `APP_BASE_URL`
- broker public base URL: `NEXT_PUBLIC_HACK_AUTH_BROKER_URL`, `HACK_AUTH_BROKER_URL`, `AUTH_BROKER_PUBLIC_BASE_URL`
- broker internal/proxy base URL: `HACK_AUTH_BROKER_INTERNAL_URL`, `AUTH_BROKER_INTERNAL_URL`
- trusted origins: `BETTER_AUTH_TRUSTED_ORIGINS`
- local routed-host inference override: `HACK_LOCAL_DEV_HOST`, `NEXT_PUBLIC_HACK_LOCAL_DEV_HOST`

When verifying provider parity or browser handoff behavior, prefer these variables over introducing app-specific aliases outside the shared auth-config path.

## Tooling Notes
## Local-first Assumptions

- Bun is the canonical runtime and validator path for this repo.
- The local machine currently has Bun available, but the installed version may lag the version declared in `package.json`; prefer repo commands and keep validation evidence concrete.
- Use `./dist/hack` for repo-bound CLI behavior after build; use global `hack` only for runtime orchestration.
- Repo-external Bun smoke scripts are a poor fit for monorepo validation here: if a smoke needs workspace imports such as `@hack/auth-contract`, keep the script under the repo root or use an existing repo-resident entrypoint instead of generating it under `/tmp`.
- Bun/WHATWG URL parsing normalizes dot segments before most handlers inspect `req.url` or `URL.pathname`; security-sensitive route validation that needs to reject raw traversal attempts cannot rely on normalized pathname checks alone.

For outage-mode proofs of repo-local CLI fallback behavior, point broker traffic at a dead local address with `HACK_AUTH_BROKER_URL=http://127.0.0.1:9` and set `HACK_SETUP_SYNC_MODE=off` so setup-sync noise does not mask the intended offline signal.

## Auth-Broker Test Isolation
- Use repo-local commands and `./dist/hack` for current-branch CLI behavior after build.
- Use global `hack` only when validating installed runtime orchestration intentionally.
- Do not add new required hosted services, auth brokers, web dashboards, GitHub apps, or Linear credentials to the supported path.

- Bun loads repo-root `.env` / `.env.local`, so auth-broker tests should clear broker-related env before asserting defaults or failure guidance.
- `services/auth-broker/tests/test-env.ts` provides `installAuthBrokerEnvIsolation()` for suites and `withIsolatedAuthBrokerEnv()` for per-test overrides.
- Those helpers set `HACK_AUTH_BROKER_DISABLE_ROOT_ENV_FALLBACK=true`, which disables `services/auth-broker/src/config.ts` fallback reads from repo-root `.env.local` and `.env` so config tests stay hermetic.
- Use `withAuthBrokerRootEnvFallback()` plus `configureRootEnvFallbackForTests()` when a regression needs deterministic fake repo-root dotenv contents without depending on ambient checkout state.
## Env and Secrets

## Env Status Taxonomy
- Canonical shared env files: `.hack/hack.env.default.yaml` and `.hack/hack.env.<overlay>.yaml`.
- Worktree-local override files: `.hack/hack.env.local.yaml` and `.hack/hack.env.<overlay>.local.yaml`.
- Derived compatibility files: `.hack/.env` and `.hack/.env.state.json`.
- Local checkout key: `.hack.secret.key`.
- Linked-worktree shared key: stored under the git common dir so sibling worktrees can decrypt committed secrets.
- Portable/CI/container key: `HACK_ENV_SECRET_KEY`.

- `trust_model` answers whether env state is local-only, plaintext-compatible, or broker/shared.
- `custody` answers who currently holds the sensitive material (for example local secret backend vs broker-managed).
- `portability` answers whether the current representation can move safely across machines.
- `shared_state` is the cross-surface summary used by CLI/API/web status views; treat unknown or command-error states explicitly instead of relabeling them as local-only.
## Managed Containers

## Secret Storage Notes
- Use `hackdance/hack:slim` for repo-local managed-agent containers when Docker Hub is available.
- Inject `HACK_ENV_SECRET_KEY` from the runtime or secret manager; never bake `.hack.secret.key` into an image.
- Slim/codex mode should use repo-local commands such as `hack env list`, `hack host exec`, `hack host shell`, and `hack tickets`.
- Machine-wide surfaces such as `hack global install`, Caddy/CoreDNS/Loki/Grafana, and local CA bootstrap are not expected in slim mode.

- `HACK_SECRETS_DISABLE_KEYCHAIN_FALLBACK=true` disables encrypted-file fallback to keychain-backed material; use it when recovery tests must prove there is no silent downgrade to local keychain access.
## Runtime and Lifecycle

## Trusted-Origin Inventory
- Prefer `hack doctor` and `hack doctor --fix` before manual runtime/network repair.
- For lifecycle changes, verify `sh -c` semantics, stdin behavior, process-group cleanup, stale mux metadata recovery, and singleton listener handling.
- For daemon/gateway request-target hardening, use an isolated temp-HOME repo-built daemon (`bun index.ts daemon start --foreground`) when live proof would otherwise mutate shared user daemon state.

Use one explicit allowlist model for auth/session flows:
- local routed hosts: `https://hack-cli.hack`, `https://*.hack-cli.hack`, `https://hack-cli.hack.gy`, `https://*.hack-cli.hack.gy`
- local broker smoke URL only where direct HTTP validation is required: `http://127.0.0.1:8080`
- deploy-ready web origins from environment for Vercel preview/production (for example `https://<preview-domain>` and `https://<production-domain>`); do not hardcode a second deploy domain path outside env/config
## Outage and Drift Proofs

Trusted-origin tests should exercise one allowed routed host, one allowed deploy-ready env-supplied origin, and one rejected untrusted origin.
- For stale env compatibility output, use `hack doctor` and `hack env materialize`.
- For stale lifecycle state, use `hack doctor`, then `hack down`, then rerun `hack doctor`.
- For tickets remote auth failures, prefer explicit SSH guidance and bounded timeouts over interactive prompts.
Loading
Loading