# Issue #755 — Logical Mos identity and connector lease fencing - Task: `MOS-PORT-M1-001` - Branch: `feat/mos-logical-identity-fencing` - Base: `origin/main` - Started: 2026-07-14 - Working budget: 38K tokens (task ledger estimate); one implementation lane, bounded to M1. ## Objective Implement the first runtime-portability security boundary: normalized logical-agent identity plus a PostgreSQL-durable exclusive connector lease and server-validated fencing grants. ## Scope - Normalized identity contract independent of harness/provider-native session IDs. - DB migration/schema/repository for one lease per tenant/logical-agent/binding. - CAS acquire/takeover, monotonic epoch, TTL, heartbeat, release, expiry handling. - Server-derived grants bound to tenant, logical agent, binding, connector, scopes, expiry, and lease epoch. - Reject and credential-safely audit stale, expired, forged, unauthorized, cross-tenant, and cross-binding grants before adapter side effects. - Runtime adapter boundary consumes normalized lease context. - Unit, migration, close/reopen, concurrency, abuse, and gateway integration tests. - Required developer/operations documentation for schema and security behavior. ## Explicit exclusions No checkpoint/handoff payloads, exactly-once journal/receipts, concrete Claude/Pi/Codex harness adapter, channel cutover, or full cross-harness failover E2E. ## Reconciliation baseline - Prospective remediation handoff: `web1:coder1`; PR [#757](https://git.mosaicstack.dev/mosaicstack/stack/pulls/757), issue [#755](https://git.mosaicstack.dev/mosaicstack/stack/issues/755). - Before rebase: `dff8ce4f79ef90370c29d925002118a708010091`; required base: `origin/main` at `2e2280070ae67288be45f41743cf67052a8ca5a6`; original branch base: `d0771835542deab048ad8e79f271e3abdb6151f7`. - Provider metadata: #757 is open, targets `main`, head is `feat/mos-logical-identity-fencing`, prior CI is green, and the provider reports it is not mergeable because of conflicts. - Affected delivery paths: `apps/gateway/src/agent/agent.module.ts`; connector-lease gateway repository/service and three focused tests; `packages/agent/src/connector-lease.ts` plus test/export; `packages/types/src/agent/connector-lease.dto.ts` plus test/export; `packages/db/src/schema.ts`, migration `0016_salty_morlocks.sql`, Drizzle snapshot/journal; `docs/PRD.md`, `docs/SITEMAP.md`, MOS architecture/operations pages, this scratchpad, and `docs/tess/TASKS.md`. - Read-only merge-tree inspection found only `docs/PRD.md` and `docs/SITEMAP.md` conflicts. Current-main #756 channel contracts, #758 roster-v2 structural compiler, and Native Kanban SOT use distinct contract domains; no substantive architecture collision was identified before mechanical reconciliation. - Reconciliation constraints: retain all current-main #752/#756/#758/KBN content; add only nonduplicative #755 references; do not alter semantics or conflate connector leases/grants with Kanban task leases/fences, local Fleet leases, auth sessions, ResetSession generations, or federation grants. ## Plan (TDD RED → GREEN → REFACTOR) 1. Map existing contracts, DB/migration conventions, gateway authorization/audit boundaries, and test infrastructure. 2. Add failing contract/repository/concurrency/restart/abuse/gateway tests and capture RED evidence. 3. Implement the smallest normalized contracts, schema/migration/repository, grant validator, audit sink, and gateway service/adapter boundary needed to pass. 4. Refactor for clear invariants and credential-safe observability; rerun focused suites. 5. Run package/repo typecheck, lint, format, and appropriate tests. 6. Run independent code + security review, remediate, and re-review. 7. Inspect the final diff for security/scope drift; commit; queue guard; push; open PR with `Refs #755` and exact verification; stop without merge/issue closure. ## Constraints and safety notes - `docs/tess/TASKS.md` is orchestrator-only and will not be edited. - Existing dirty `.mosaic/orchestrator/mission.json` and `.mosaic/orchestrator/session.lock` are launcher/orchestrator state and will not be staged or altered intentionally. - No client-supplied identity may confer authority. - No credential, token, or raw grant material may be persisted to audit/log output. - Existing authorization checks remain intact; fencing is an additional fail-closed layer. ## Assumptions resolved from existing architecture - `ASSUMPTION:` M1 exposes no public lease endpoint. The gateway service is an internal policy surface with deny-all default policy because concrete connector activation/cutover is explicitly deferred. - `ASSUMPTION:` Fencing epochs use PostgreSQL `bigint` and cross-module decimal strings, preserving JSON portability without JavaScript number precision loss. - `ASSUMPTION:` Process-local grant provenance intentionally fails closed across restart; durable lease/epoch state survives and fresh grants require current policy + lease validation. ## TDD evidence RED observed before implementation: - `corepack pnpm --filter @mosaicstack/types exec vitest run src/agent/connector-lease.dto.spec.ts` → failed to load missing `connector-lease.dto.js`. - `corepack pnpm --filter @mosaicstack/agent exec vitest run src/connector-lease.test.ts` → failed to load missing `connector-lease.js`. - Gateway focused tests failed before implementation because the new repository/service boundaries did not exist (workspace dependencies were then built before behavioral GREEN runs). GREEN to date: - Types contract: 6/6 passed. - Agent grant/fencing unit suite: 5/5 passed. - Gateway PGlite repository + policy/side-effect integration: 7/7 passed; 1 real-PostgreSQL test skipped when `DATABASE_URL` absent. - Real PostgreSQL focused run with configured `DATABASE_URL`: 1/1 passed (credential value not emitted in reports). ## Documentation checklist - [x] `docs/PRD.md` contains current MOS-PORT M1 scope and acceptance criteria. - [x] Developer architecture: `docs/architecture/mos-runtime-portability-m1.md`. - [x] Admin/operations guidance: `docs/guides/mos-connector-lease-operations.md`. - [x] `docs/SITEMAP.md` links both pages. - [x] No user-guide change: M1 exposes no user-facing flow or channel cutover. - [x] No OpenAPI/endpoint-index change: M1 adds no HTTP endpoint. - [x] Migration/restart/rollback safety and credential-safe audit constraints documented. - [x] Canonical source remains in-repo; no external publishing action is in scope. - [x] Independent review confirms documentation matches implementation; implementation-specific findings were remediated. ## Independent review and remediation Codex code/security review ran in multiple rounds. Findings and root-cause remediations: 1. Policy could not inspect requested scope/TTL → policy subject now receives normalized requested scopes and explicit requested TTL. 2. Unbounded authority lifetime → hard defaults cap leases at 5 minutes and grants at 30 seconds; overrides may only tighten; over-limit tests added. 3. Cross-tenant denial could audit under submitted tenant → mismatch audit uses authenticated tenant plus sanitized `untrusted` target metadata; integration assertion added. 4. Malformed forged grant could break the denial/audit path → runtime-safe shape validation with sanitized fallback audit; malformed-input test added. 5. Gateway integration test depended on prior test state → denial test now seeds a unique binding itself; isolated `-t` run passed. 6. Reviewer repeatedly identified launcher-generated `.mosaic/orchestrator/*` state; those files remain unstaged and excluded from the implementation commit. Latest independent security review: no critical/high/medium/low findings. Final commit-level code review remains to run after the intended diff is committed without launcher state. ## Verification evidence - Focused contracts/fencing: types 6/6; agent 9/9. - Gateway focused PGlite repository/policy integration: 7/7; isolated denial test 1/1. - Real PostgreSQL close/reopen/CAS test: 1/1 with configured `DATABASE_URL`. - Root `corepack pnpm typecheck`: 42/42 Turbo tasks passed. - Root `corepack pnpm lint`: 23/23 Turbo tasks passed. - Root `corepack pnpm format:check`: all matched files passed. - Root `corepack pnpm test`: 42/42 Turbo tasks passed; gateway 616 passed / 12 environment-gated skipped; DB 19 passed / 7 environment-gated skipped; Mosaic 650 passed. ## Known residual risks - Concrete connector policies and Claude/Pi/Codex adapters are intentionally deferred; production policy defaults deny-all. - Gateway pre-side-effect validation cannot make an external system exactly-once. Adapters must propagate/enforce the epoch at downstream effect boundaries; receipts/journaling are later #754 scope. - Migration rollback is additive-only; dropping lease/audit tables is intentionally manual to avoid destroying authority/audit evidence. ## Commit-level review remediation - Commit-level Codex code review found one `should-fix`: heartbeat, release, and grant issuance authorized caller-supplied lease fields before canonical normalization. - TDD RED: the isolated gateway policy-boundary test showed mixed-case/padded logical agent, binding, connector, scope, and epoch values reaching policy unchanged. - Remediation: exported the coordinator's canonical lease normalizer and applied it at the gateway boundary before tenant/policy checks and coordinator dispatch for heartbeat, release, and grant issuance. - GREEN: isolated policy test 1/1; focused types 6/6, agent 9/9, gateway 8/8; root typecheck 42/42, lint 23/23, format check passed, and root tests 42/42 (gateway 617 passed / 12 environment-gated skipped). - Commit-level security review remained clean: no critical/high/medium/low findings. ## Durable grant-expiry review remediation - Final commit review found a second `should-fix`: grant expiry was capped against submitted lease metadata after current-authority validation, rather than the durable lease row. - TDD RED: a crafted same-authority lease with a later submitted expiry produced a grant expiring after the durable row. - Remediation: grant authority fields and expiry now derive from the durable current lease; submitted scopes remain an additional narrowing constraint. - GREEN: focused agent fencing suite 10/10. ## Current-main reconciliation (2026-07-14) - Rebased the existing PR branch from `dff8ce4f79ef90370c29d925002118a708010091` (old base `d0771835542deab048ad8e79f271e3abdb6151f7`) onto `origin/main` `2e2280070ae67288be45f41743cf67052a8ca5a6`; current uncommitted reconciliation head is `d190732a550918161b91d3eb54640f4ea0e2e499`. - Resolved only `docs/PRD.md` and `docs/SITEMAP.md`: preserved current-main #752 Native Kanban, #756 official-channel, and #758 FCM material; retained the nonduplicative #755 M1 workstream and placed its two documentation links in the existing Runtime-neutral Mos section. No #755 source semantics changed. - Compatibility review confirmed separate authority domains: connector lease/epoch/grant remains distinct from KBN task leases/fences, local Fleet roster lifecycle, auth sessions, ResetSession context, and federation grants. No channel cutover, adapter activation, checkpoint/exactly-once behavior, UI convergence, or #754 expansion was added. - Focused verification after building the required workspace dependencies: types contract 6/6; agent fencing/grant 10/10; gateway repository/PGlite and policy integration 8/8. The focused real-PostgreSQL test was skipped because `DATABASE_URL` was not configured; no credentials were inspected or emitted. - Generated schema check: `pnpm --filter @mosaicstack/db db:generate` reported no schema changes; migration `0016_salty_morlocks`, snapshot, and journal were unchanged by generation. - Full verification: `pnpm typecheck` 42/42; `pnpm lint` 23/23; `pnpm format:check` passed; `pnpm test` 42/42 (gateway 627 passed / 12 environment-gated skipped). Scoped diff check and local documentation-link scan passed. - Pending only: commit this reconciliation record, queue guard, force-with-lease push of the rebased existing branch, then fresh independent DB/code/security review and Ultron. Do not claim merge or issue closure. ## Durable lifecycle-authority remediation (2026-07-14) - Independent review found that heartbeat and release authorized the submitted lease, allowing forged lifecycle scope data to influence policy before the durable row was consulted. - Remediation: lifecycle operations tenant-check the submitted lease, load the durable current lease, compare tenant, logical agent, binding, lease UUID, connector, epoch, and canonical ordered scopes, audit and deny any absent/mismatched authority, then run policy and coordinator lifecycle calls with the durable lease. Coordinator CAS/fencing checks remain unchanged. - Adversarial gateway integration coverage proves forged `tool.execute` scopes on a durable `runtime.send` lease deny before policy or mutation, preserve heartbeat/release state, and write a denial audit for both lifecycle actions; canonical heartbeat and release remain accepted. - Verification: focused types 6/6; agent 10/10; gateway PGlite integration/repository 9/9 with one `DATABASE_URL`-gated PostgreSQL test skipped; Drizzle check passed; root typecheck 42/42; lint 23/23; format check passed; root test 42/42 (gateway 628 passed / 12 environment-gated skipped). - Pending: commit, queue-guard, force-with-lease push, then independent DB/code/security rereview and CI. Do not merge or close #755.