Skip to content

REVIVAL-01: Gate registration by deployment mode - #1334

Merged
Chris0Jeky merged 13 commits into
mainfrom
issue-1297/registration-gating
Jul 13, 2026
Merged

REVIVAL-01: Gate registration by deployment mode#1334
Chris0Jeky merged 13 commits into
mainfrom
issue-1297/registration-gating

Conversation

@Chris0Jeky

@Chris0Jeky Chris0Jeky commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Do not merge tonight

This PR changes authentication policy conventions and adds a migration. Per the overnight authority, it requires maintainer review and is not eligible for autonomous merge.

Summary

  • Add Auth:Registration:Mode with Open (application default), InviteOnly, and Closed.
  • Require an operator-minted invite for the first account in restrictive modes, eliminating remote first-user takeover while preserving Open first-user behavior.
  • Assign Owner only to the transaction that wins the persisted first-user bootstrap claim; later accounts remain server-sanitized Editor.
  • Add expiring, single-use, SHA-256-hashed invite codes and taskdeck invite create --expires N.
  • Authorize restrictive registration before checking username/email uniqueness, preventing the anonymous account-enumeration oracle while keeping invite consumption and uniqueness in one rollback-safe transaction.
  • Move password BCrypt work outside the SQLite write transaction and reject ineligible restrictive requests before paying the hash cost.
  • Disable the authenticated POST /api/users creation bypass in restrictive modes; /api/auth/register is the single policy-enforced creation surface there.
  • Gate only new password/external identities; existing password and linked OAuth/OIDC users continue to sign in in every mode.
  • Preserve first-owner bootstrap when an upgraded database contains only the production CLI system actor.
  • Enforce the documented inclusive 1-minute to 365-day invite lifetime, including rejecting bare --expires without minting an invite.
  • Package the CLI in both container images, set public deployment templates to Closed, and document the private-shell/non-root first-owner ceremony.

Assumption: v0.1 invite minting is local-operator CLI-only, centralized behind IRegistrationPolicyService. Reason: #1297 explicitly permits CLI-only and this is smaller than a new remote admin endpoint. Reversible by adding an authenticated admin transport over the same service.

Verification

Passed on final local commit 5655f36b:

  • Focused registration/auth application tests: 58/58
  • Focused registration/auth/migration API tests: 35/35
  • Focused invite CLI tests: 5/5
  • Order-independent Editor authorization regressions: 3/3
  • Concurrent single-invite redemption after the eligibility and role changes: 10/10 repeated; every run persisted exactly one redeemer
  • Full serialized backend suite: 6,877 passed, 0 failed, 1 known skipped (INV-09 architecture invariant)
    • Domain 1,625; Application 3,253; API 1,859; CLI 100; Architecture 20 passed/1 skipped; Integration 20
  • node scripts/check-docs-governance.mjs
  • git diff --check
  • Earlier unchanged container packaging proof: docker compose ... config --quiet with required test secrets

The first full run of the final review batch exposed an order-dependent API test fixture that assumed the first account was Editor. The fixture was made bootstrap-aware before commit; the focused 3/3 regression and final full suite are green. Two earlier unrelated full-suite failure families remain tracked in #1282 and #1335 and are not dismissed.

GitHub status after this push:

  • Required CI and CodeQL are running on 5655f36b; the earlier head 6156022b was 20/20 green, including Linux/Windows API integration and E2E Smoke.
  • CI Extended has a shared workflow startup failure tracked in CI Extended startup-fails at reusable Gitleaks permission boundary #1330. This PR is not fully green while that required/no-failure policy remains unresolved.

Review fixes

  • 1b2a7f13: fixes the first four fresh FULL-tier findings—account enumeration, remote bootstrap takeover, BCrypt transaction duration, and missing real-store/concurrency coverage.
  • 6156022b: updates operator, container, cloud, authentication, configuration, and data-model guidance.
  • 6699a9d9: fixes the production CLI-actor migration identity and proves a CLI-only upgrade can redeem the first-owner invite.
  • 594f7de1: adds cheap restrictive-policy eligibility before hashing, retains atomic transactional authorization, and enforces/tests invite lifetime boundaries.
  • 5655f36b: closes the authenticated /api/users bypass, derives the first Owner from the transactional bootstrap winner, rejects bare --expires, and documents the REVIVAL-05: Paper onboarding - guided first-board path + Login/Register in the default theme #1301 browser dependency with an explicit API redemption path.
  • Every existing inline thread, including both later duplicate connector findings, has a direct reply and is resolved.
  • Gemini's static process-global bootstrap cache suggestion was invalidated with cross-process/multi-database/restore evidence and resolved; the database singleton remains authoritative.

Review notes

Checklist

  • Behavior changes have unit/integration/concurrency coverage
  • Migration has fresh/existing-database coverage
  • Restrictive first-owner ceremony is local/operator-authorized
  • Documentation updated
  • Fresh FULL-tier findings and all existing bot threads addressed with evidence
  • Required CI green on final commit
  • Fresh Codex review round on final commit complete
  • Maintainer approval for auth-policy convention/migration

Closes #1297

Copy link
Copy Markdown
Owner Author

Self-review findings

I reviewed the complete diff against #1297, including the auth boundary, migration/bootstrap behavior, container packaging, deployment defaults, tests, and all current PR comments/reviews (none existed when this review began).

MEDIUM — fixed: container invite command could change SQLite file ownership

The initially documented container command ran the packaged CLI as root. On SQLite deployments that can leave WAL/journal sidecars owned by root, preventing the non-root API process from reopening the database.

Fix evidence: commit 37253d45 changes Compose examples to UID 10001:10001 and production-image examples to gosu taskdeck.

Verification: the packaged CLI minted an invite successfully as UID 10001 in taskdeck-registration-api:test and UID 1001 in taskdeck-registration-prod:test; docs governance and git diff --check also pass.

No other actionable findings were identified in this self-review. The PR remains blocked from merge pending required CI, a fresh independent adversarial review, and maintainer approval for the auth-policy convention/migration. The full-suite failures listed in the PR body also remain explicitly unresolved and are not being treated as green.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces registration gating to Taskdeck, allowing operators to configure the registration mode as Open, InviteOnly, or Closed. It adds a RegistrationPolicyService to authorize new users, supports atomic first-user bootstrapping, and implements CLI commands to generate one-time registration invites. The feedback highlights opportunities to optimize performance and resource utilization by moving CPU-intensive password hashing operations outside of active database transactions in AuthenticationService. Additionally, it is recommended to cache the claimed state of the first-user bootstrap in-memory within RegistrationPolicyService to avoid redundant database write attempts on subsequent registrations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread backend/src/Taskdeck.Application/Services/AuthenticationService.cs Outdated
Comment thread backend/src/Taskdeck.Application/Services/AuthenticationService.cs
Comment thread backend/src/Taskdeck.Application/Services/RegistrationPolicyService.cs Outdated

Copy link
Copy Markdown
Owner Author

Out-of-scope full-suite failures are now durably tracked rather than dismissed:

The exact tests passing narrowly does not make either full run green. This PR remains never-merge and still requires a fresh independent FULL-tier review, final CI, and maintainer approval.

@Chris0Jeky Chris0Jeky left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fresh FULL-tier adversarial review — changes requested

I reviewed #1297's acceptance criteria, the complete 49-file diff at 37253d45, auth/controller/service/SQLite transaction boundaries, migration upgrade/fresh-install behavior, CLI and container packaging, deployment guidance, targeted/full-suite evidence, and every current top-level/review/bot comment.

CRITICAL

No critical finding.

HIGH

  1. Restrictive modes expose an account-enumeration oracle. AuthenticationService.RegisterAsync checks Users.ExistsAsync at backend/src/Taskdeck.Application/Services/AuthenticationService.cs:101-103 before the registration policy at lines 105-115. After bootstrap, a caller to a Closed instance (or InviteOnly without a valid invite) gets 409 plus “account ... already exists” for a registered username/email but the stable 403 for an unused identity. That defeats the generic login contract and lets an unauthenticated remote caller enumerate accounts. Expected fix: authorize the restrictive policy before returning uniqueness information, while keeping invite consumption and the uniqueness check in the same transaction so a duplicate attempt rolls the invite back; add API regressions proving known and unknown identities receive the same denial without a valid authorization.

  2. The documented “safe public-deployment default” permits remote first-user seizure. RegistrationPolicyService.AuthorizeNewUserAsync unconditionally authorizes whichever request first inserts the bootstrap singleton (RegistrationPolicyService.cs:38-42), including a remote request. The Railway/Render guidance then exposes a fresh instance at a public URL and labels Closed safe (deploy/railway.toml:8-15, CLOUD_DEPLOYMENT_GUIDE.md:77-110). A scanner that reaches the service before the operator can become the only user; Closed then locks the legitimate operator out. The literal no-lockout AC is satisfied, but the resulting public-bootstrap ceremony is not safe. Expected fix: require a bootstrap secret/local operator action, or remove the public-safety claim and require private access controls until the owner account is created. Cover the authorized-vs-unauthorized bootstrap path.

  3. BCrypt work is performed while a SQLite write transaction is open. The registration policy executes a write before BCrypt.HashPassword at AuthenticationService.cs:118; the new-external-user path also hashes at line 230 inside its transaction. That lengthens the single-writer lock and can turn registration/OAuth concurrency into database-wide timeouts. Gemini's two HIGH threads (3567833413, 3567833416) are valid and this finding adopts them. Expected fix: redesign the short transaction so expensive hashing occurs outside the write lock without weakening atomic bootstrap/invite consumption, and add a contention/rollback regression.

MEDIUM

  1. The explicit FULL-tier integration coverage is incomplete. RegistrationGatingApiTests integrates password registration modes/bootstrap and only sequential invite reuse. The external-login gate exists only as a mocked application test, and no integration test proves a new OAuth/OIDC identity is denied while an existing linked identity still signs in against the real policy/store. Likewise, single-use invite redemption is not raced, so the atomic SQL guarantee is unproved at the API boundary. Expected fix: add real-store integration coverage for new-vs-existing external identities in restrictive modes and concurrent redemption of one invite (exactly one success, one stable 403, no orphan user/link).

LOW

No low finding.

Existing comments and bot triage

  • The self-review's container ownership finding is fixed by 37253d45; the non-root command evidence is adequate.
  • The out-of-scope full-suite failures are durably tracked in #1282 and #1335; they are not treated as green.
  • Gemini's two BCrypt threads are valid and covered by HIGH #3.
  • Gemini's proposed process-global static _bootstrapClaimed cache (3567833418, 3567833422) is invalid. One process can host multiple database paths/test factories, a database can be restored/replaced, and separate API/CLI processes share SQLite. A static flag would let one database's state suppress bootstrap checks for another and would make the process cache—not the transactionally persisted singleton—the authority. Do not implement that suggestion; optimize only with database-scoped, rollback-safe state if measurement justifies it.
  • The current frontend cannot submit inviteCode, but #1301 explicitly owns and sequences that UI work after #1297; treat it as a merge/deployment dependency rather than silently claiming this PR is independently user-complete.

Verification

  • Fresh reviewer run: application auth/registration/external/OIDC/account-linking filter 90/90 passed.
  • Fresh reviewer run: registration-gating + migration API filter 14/14 passed.
  • Fresh reviewer run: invite CLI filter 4/4 passed.
  • GitHub checks at submission: all completed jobs green; Windows API Integration still pending.
  • The PR body correctly records two non-green full backend runs. This review does not approve merge; the PR remains maintainer-only and changes are requested.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

FULL-tier review fix evidence — final head 6156022b

All findings from review #1334 (review) and all four pre-push bot threads are addressed.

  1. HIGH — account enumeration: 1b2a7f13 authorizes restrictive registration before username/email uniqueness disclosure. Known and unknown identities now receive the same stable 403 without a valid invite in both Closed and InviteOnly. Valid-invite duplicate attempts roll back consumption.
  2. HIGH — remote first-user takeover: 1b2a7f13 requires an operator-minted invite for the first owner in both restrictive modes; Open remains unchanged. 6156022b documents private-shell, non-root provisioning and removes unconditional-bootstrap claims.
  3. HIGH — BCrypt inside SQLite transaction: 1b2a7f13 precomputes password hashes before BeginTransactionAsync for password and new-external registration. Invite/bootstrap authorization, uniqueness, and writes remain atomic.
  4. MEDIUM — missing real-store/concurrency proof: 1b2a7f13 adds real SQLite coverage for new-vs-existing external identities, concurrent redemption of one invite (exactly one persisted user), duplicate rollback/reuse, and restrictive known-vs-unknown denial.
  5. Gemini static cache suggestions: invalidated, not implemented. A process-global flag is unsafe across multiple database paths, restores/replacements, and API/CLI processes; the persisted database singleton remains authoritative.

Verification on the pushed commits:

  • build green;
  • 152/152 focused application auth tests;
  • 43/43 API auth/registration/migration tests;
  • 99/99 CLI tests;
  • concurrent redemption 10/10 repeated;
  • full serialized backend: 6,855 passed, 0 failed, 1 known INV-09 skip;
  • docs governance, diff check, and Compose render green.

Every existing inline thread has a direct reply and is resolved. Required CI and the fresh connector review on 6156022b are still pending. CI Extended remains a shared workflow startup failure tracked by #1330, so this PR is not described as fully green and remains never merge tonight.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

@codex review — fresh FULL-tier pass requested on final head 6156022 after the batched auth, transaction, integration-test, and operator-doc fixes. Please review the complete diff independently; this PR remains maintainer-only and must not be merged tonight.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Final CI update for head 6156022b:

Therefore the required suite is green, but the PR is not described as fully green under the zero-failure policy and remains maintainer-only / never merge tonight.

@chatgpt-codex-connector

Copy link
Copy Markdown

Fresh FULL-tier review result for PR #1334 (6156022b)

I reviewed the trigger, PR metadata/comments, and the local diff at HEAD = 6156022bb6f2b553442531a0df1b448a73565f8e. I found no new actionable findings requiring a follow-up code PR.

Review findings

CRITICAL

  • No findings.

HIGH

  • No findings.

MEDIUM

  • No findings.

LOW

  • No findings.

Evidence reviewed

CI / PR status interpretation

  • The trigger reports Required CI as 20/20 green and CodeQL green on 6156022b.
  • CI Extended still has a reusable-workflow startup failure tracked in CI Extended startup-fails at reusable Gitleaks permission boundary #1330, so under the repo’s zero-failure policy this PR should still not be described as fully green.
  • The PR remains maintainer-only / never-merge-tonight per the trigger and PR body.

Commands run

  • bash scripts/check-git-env.sh — completed, with a repo-documented warning that /usr/bin/git appears to be Cygwin/MSYS2-style git in this environment.
  • git status --short --branch
  • git rev-parse HEAD
  • git branch -vv
  • git show --stat --oneline --decorate --no-renames --find-renames HEAD
  • git diff --stat 68f218cf053a0486285e71316b4849e2af061400...HEAD
  • rg -n "class RegistrationPolicyStore|AddRegistrationGating|TryClaimFirstUserBootstrap|RegistrationInvite" backend/src backend/tests deploy docs | head -200
  • ⚠️ gh pr view 1334 --json number,title,headRefOid,baseRefOid,comments,reviews,reviewDecision,statusCheckRollup --commentsgh is not installed in this container, so I relied on the provided PR comment blocks plus local repository inspection.
  • ⚠️ dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~RegistrationPolicyServiceTests|FullyQualifiedName~AuthenticationServiceTests|FullyQualifiedName~ExternalLoginTests"dotnet is not installed in this container.
  • ⚠️ dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~RegistrationGatingApiTests|FullyQualifiedName~MigrationBootstrapTests"dotnet is not installed in this container.
  • git status --short — clean working tree.

Commit / PR creation

No code or documentation changes were needed, and the working tree is clean. Per the repository instruction not to call make_pr without changes, I did not commit or create a new pull request.

Outstanding maintainer-owned tasks surfaced from OUTSTANDING_TASKS.md

Open items remain visible and were not modified:

View task →

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Fresh FULL-tier adversarial re-review — changes requested

I independently reviewed issue #1297, the complete 49-file diff at exact head 6156022bb6f2b553442531a0df1b448a73565f8e, the repaired auth/bootstrap/transaction/race paths, migration and CLI packaging, deployment guidance, all existing top-level comments/reviews/bot threads, and final CI.

CRITICAL

No critical finding.

HIGH

  1. The upgrade migration mistakes the real CLI system actor for a human owner and can permanently lock a CLI-first database in Closed mode. 20260713022601_AddRegistrationGating.cs:47-54 excludes only cli@system.taskdeck, and MigrationBootstrapTests.cs:169-180 repeats that obsolete fixture. The actual persisted identity is CliActorIdentity.ActorEmail = cli-actor@system.taskdeck (CliActorIdentity.cs:19), created by the boards/API-key CLI handlers. Therefore an installation that used the CLI before creating a real account gets a committed RegistrationBootstraps row during upgrade. In Closed, AuthorizeNewUserAsync then sees claimedBootstrap == false and rejects before consuming even an operator-minted invite, leaving no path to create the first real owner without changing configuration or editing the database. Expected fix: make the migration exclude the actual CLI system identity (preferably the complete reserved @system.taskdeck actor boundary), update the migration regression to use the production identity, and prove a CLI-actor-only upgraded database can still redeem the one first-owner invite in Closed. Fresh proof: the existing one-test filter passes against its wrong fixture, while executing the migration predicate with cli-actor@system.taskdeck produces actual_cli_actor_bootstrap_rows=1.

MEDIUM

  1. Every denied restrictive registration now pays the full BCrypt cost before policy rejection. AuthenticationService.RegisterAsync:101-109 hashes the caller's password before AuthorizeNewUserAsync can return the stable 403. Consequently Closed after bootstrap, InviteOnly with no/malformed invite, and random well-formed invalid invite attempts all consume an expensive BCrypt operation. The endpoint is anonymous; the per-IP limiter bounds one address but does not remove distributed CPU exhaustion, and this issue exists specifically to close the public signup abuse/cost surface. Moving hashing out of the SQLite transaction fixed lock duration but moved the cost ahead of the cheap denial boundary. Expected fix: add a cheap, non-consuming policy eligibility/preflight before BCrypt, then repeat the authoritative bootstrap claim/invite consumption inside the short transaction after hashing so races and rollback remain safe. Add a regression that denied missing/malformed/invalid invites do not invoke the expensive hasher (or otherwise prove an equivalent bounded-cost denial path), while retaining the concurrent single-use test.

LOW

  1. The invite lifetime validation does not enforce its documented one-minute minimum. RegistrationPolicyService.CreateInviteAsync:79-83 rejects only <= 0 while returning the contract “between 1 minute and 365 days.” Positive sub-minute values can pass this check and create an effectively unusable invite or race into the entity's “must be in the future” exception instead of returning the advertised validation result. The CLI currently supplies whole days, so this is latent in the service contract rather than the shipped CLI path. Expected fix: reject values below TimeSpan.FromMinutes(1) and cover the lower boundary.

Existing comments, links, and CI

This review does not approve or merge the PR. It remains draft, maintainer-only, and changes are requested for all findings above.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6156022bb6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/src/Taskdeck.Application/DTOs/UserDtos.cs
Comment thread docs/platform/CONFIGURATION_REFERENCE.md Outdated
Comment thread backend/src/Taskdeck.Application/Services/AuthenticationService.cs Outdated
Comment thread docs/api/AUTHENTICATION.md
Comment thread backend/src/Taskdeck.Cli/Commands/InvitesCommandHandler.cs
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

FULL-tier re-review fix evidence — final head 594f7de1

Follow-up to #1334 (comment). All three findings were fixed in one batched push:

  1. HIGH — CLI-only upgrade lockout: 6699a9d9 aligns the migration predicate with the production CLI actor cli-actor@system.taskdeck. The migration regression now upgrades a database containing that actual actor, mints an invite, and proves a Closed first-owner transaction claims bootstrap, consumes the invite, and persists the real owner.
  2. MEDIUM — BCrypt before restrictive denial: 594f7de1 adds a read-only policy eligibility preflight before the injectable password hasher, while retaining the authoritative bootstrap claim/invite consumption and uniqueness check in the short transaction. Password and new-external unit regressions prove policy-denied requests never invoke the hasher, open a transaction, query identity existence, or write users/links. The final transactional check still owns every race.
  3. LOW — invite lifetime floor: 594f7de1 enforces the inclusive 1-minute to 365-day contract and covers below-minimum, exact-minimum, exact-maximum, and above-maximum boundaries.

Verification on 594f7de1:

  • focused application registration/auth tests: 56 passed, 0 failed;
  • focused API registration/auth/migration tests: 33 passed, 0 failed;
  • concurrent single-invite redemption: 10/10 repeated passes after the preflight change;
  • full serialized backend suite: 6,872 passed, 0 failed, 1 pre-existing INV-09 skip (Domain 1,625; Application 3,251; API 1,857; CLI 99; Architecture 20 passed/1 skipped; Integration 20);
  • docs governance and git diff --check 6156022b..HEAD: green.

Required CI and CodeQL are now running on the pushed head. CI Extended remains subject to the shared reusable-workflow startup defect tracked in #1330. This PR remains draft, maintainer-only, and not merge-eligible tonight.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

@codex review — fresh independent FULL-tier pass requested on exact final head 594f7de1 after the single batched push fixing the CLI-only migration lockout, restrictive denial hash cost, and invite TTL boundary. Please inspect the complete auth/migration diff and all prior comments; this draft remains maintainer-only and must not be merged tonight.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 594f7de1de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/src/Taskdeck.Application/Services/AuthenticationService.cs
Comment thread backend/src/Taskdeck.Cli/Commands/InvitesCommandHandler.cs
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

FULL-tier connector fix evidence — final head 5655f36b

All findings from both connector rounds now have direct replies and all 12 review threads are resolved.

  1. P1 — authenticated /api/users bypass: 5655f36b disables that creation path in Closed/InviteOnly and proves both modes persist no bypass user and consume no unused invite.
  2. P2 — browser cannot submit invite: explicitly tracked by REVIVAL-05: Paper onboarding - guided first-board path + Login/Register in the default theme #1301, whose acceptance criteria own the Paper Register invite field after REVIVAL-01: Registration gating - Auth:Registration Mode (Open/InviteOnly/Closed) with first-user bootstrap #1297. This PR now documents the exact API redemption command and clearly states the browser dependency; no frontend/auth-router completeness is claimed.
  3. P2 — first owner forced Editor: 5655f36b returns the persisted transactional bootstrap winner from policy authorization and assigns Owner only to that request. Unit/API tests cover winner, later account, and external identity behavior.
  4. P3 — bare --expires: 5655f36b exits with usage code 2 and proves no invite row is minted.
  5. Restrictive BCrypt cost: 594f7de1 rejects ineligible password and new-external requests before hashing while retaining the authoritative transactional authorization.
  6. CLI-only migration lockout: 6699a9d9 uses the production cli-actor@system.taskdeck identity and proves first-owner redemption after upgrading a CLI-only database.
  7. Invite TTL floor: 594f7de1 enforces and tests the inclusive 1-minute to 365-day service contract.

Verification on exact pushed head 5655f36b:

Required CI and CodeQL are running on this head. CI Extended remains the shared startup failure tracked in #1330. The PR remains draft, maintainer-only, and must not be merged tonight.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

@codex review — fresh independent FULL-tier pass requested on exact final head 5655f36b after the single batched push closing the authenticated user-creation bypass, transactional first-owner role assignment, bare --expires handling, and explicit #1301 browser deferral. Please inspect the complete auth/migration diff and every prior comment/thread. This draft remains maintainer-only and must not be merged tonight.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 5655f36bde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Required E2E failure investigation

The failed workspace preferences save failure assertion is treated as a real failure, not dismissed as flaky.

Evidence gathered:

PR #1334 changes registration/auth only, so no frontend race fix is being smuggled into this never-merge auth PR. I am rerunning the failed GitHub job now after the focused investigation. #1334 remains draft and independently blocked by #1330 and maintainer-owned auth/migration review.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Required E2E rerun result

The investigated failed job reran green at the unchanged auth head 5655f36b:

The original failure remains documented rather than erased: exact local Playwright passed 6/6 and the underlying stale-summary ordering seam is tracked in #1343 with deterministic reproduction/repair criteria. PR #1334 remains draft, never-merge tonight because it changes auth policy/migrations, and independently red in CI Extended under #1330.

Chris0Jeky and others added 2 commits July 13, 2026 22:31
…on-gating

# Conflicts:
#	backend/src/Taskdeck.Infrastructure/DependencyInjection.cs
#	backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs
#	backend/src/Taskdeck.Infrastructure/Persistence/TaskdeckDbContext.cs
#	docs/platform/CLOUD_DEPLOYMENT_GUIDE.md
@Chris0Jeky
Chris0Jeky marked this pull request as ready for review July 13, 2026 22:09
Copilot AI review requested due to automatic review settings July 13, 2026 22:09
@Chris0Jeky
Chris0Jeky merged commit 760a188 into main Jul 13, 2026
33 of 34 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-project-automation github-project-automation Bot moved this from Pending to Done in Taskdeck Execution Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

REVIVAL-01: Registration gating - Auth:Registration Mode (Open/InviteOnly/Closed) with first-user bootstrap

2 participants