You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allowed IP ranges: an IPv6 CIDR entry never matches at the sign-in gate or the save check (matcher reads IPv4 CIDR and exact addresses only), while the setting promises CIDR ranges #30320
Filing gate: ② found by the contract review of PR #30309 (card #30203, record 6650936040), outside that card's door. Read, not measured — the first act is to measure it. ⛔ Not a claim. Triage sets the grade.
Reader: domain:server, plugin-auth (auth-manager.tsipMatchesRange, isClientIpAllowed), the allowed_ip_ranges help text in service-settingsauth.manifest.ts.
Dedupe: ipv6 cidr → none open. New.
What the reading says
ipMatchesRange matches an IPv4 CIDR and an exact address (IPv4-mapped addresses folded). An IPv6 CIDR entry such as 2001:db8::/32 falls back to exact match and so never matches a real client. The setting's help text promises "CIDR ranges" unqualified. Effect: an admin who allows an IPv6 range locks out every IPv6 client in it at the sign-in gate; after PR #30309 the save check refuses that save for an IPv6 admin (fail-safe), with copy that says the list does not include an address it textually covers.
Wanted
Measure: an IPv6 client inside an allowed IPv6 CIDR, at the gate and at the save.
Either match IPv6 CIDR in ipMatchesRange (both doors share it), or refuse an IPv6 CIDR entry at save with a clear message and say "IPv4 CIDR" in the help text.
Claim: PM loop round 4
Session: session_01Utp6Cbvcmv8YWFhFJwzjKL (seat) · dev thread session_01Utp6CXwcgsAkqebfzjnfiS
Account: os-bill
Branch: claude/project-thread-g6bjfy (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-30320
Domain: domain:server
Seat: domain:server#2
File surface: packages/plugins/plugin-auth/src/auth-manager.ts (ipMatchesRange and its helpers only, or a small module it imports), tests, possibly packages/services/service-settings/src/manifests/auth.manifest.ts (help text, only on the refuse-at-save route), one changeset .changeset/30320-ipv6-cidr-allowed-ranges.md. ⛔ packages/spec/**, ui/**, apps/**, skills/**, docs/adr/**, content/docs/**, .claude/**. (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (opus) — dispatch-gates --tier: no path-derived mandate, effort: high
Clause-②: no expected — matching an IPv6 CIDR accepts clients a stored list already names; the dev re-declares yes for a new export, (narrowing) for a save-time refusal of an entry stored before
Metadata-protocol: none
Responsibility: ipMatchesRange matches IPv4 CIDR and exact addresses only, so an IPv6 CIDR entry never matches at the sign-in gate or the save check | the gate and the save check themselves (#30203, unchanged) | every deployment that allows IPv6 ranges
Thread-read: 6651976799
Premise: holds — read at d1d90b70: ipMatchesRange (:1194) falls back to exact match for a non-IPv4 range; callers are isClientIpAllowed (:5905), allowed-ip-ranges.ts (:152) and the plain-HTTP eligibility list (:558)
Serial constraints cleared: this seat's #30202 (PR #30298) and #30302 edit auth-plugin.tsapplySettings and the settings predicates; this card keeps to the matcher; pull main before PR and before landing. Sibling seats: none on plugin-auth.
Order: project file server-seat2/dispatch-30320.md, delivered as the card thread's opening message.
{
"issue": 30320,
"status": "done",
"branch": "claude/project-thread-g6bjfy",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30392",
"session": "session_01Utp6CXwcgsAkqebfzjnfiS",
"premise_still_valid": true,
"summary": "Premise measured at d1d90b70: with the new pins, 6 failed | 7 passed, both doors included (the sign-in gate refused an IPv6 client inside an allowed IPv6 CIDR, and a settings save from inside it was refused). ipMatchesRange now reads both sides as the address they denote (IPv4 as uint32, other IPv6 as eight groups through the file's existing parseIpv6Literal; IPv4-mapped dotted or hex folds to IPv4; zone id dropped) and compares the first n of 128 bits for an IPv6 range; exact IPv6 entries compare by value. Families never cross. An IPv6 prefix outside 0..128 whole numbers leaves the entry matching only its base, as the old text fallback did; IPv4 entries keep their exact earlier reading, so no IPv4 list changes what it admits. Both doors call the one matcher. Node net.BlockList measured and rejected (it matches across families).",
"tests": "New: 5 cases in auth-manager.test.ts (IP allow-list), 2 door cases in allowed-ip-ranges.pin.test.ts (real AuthPlugin + SettingsService + settings routes). pnpm --filter @objectstack/plugin-auth test: Test Files 222 passed (222), Tests 3925 passed | 10 skipped (3935) at d9855b3c and again at aa57f6dd after rebuilding the closure; typecheck exit 0. Ablations via scripts/ablation-replace.mjs (tests import src; no build between legs): A1 IPv4-branch family check dropped -> 1 failed; A2 IPv6-branch family check admits IPv4 -> 3 failed; A3 IPv6 prefix read with Number() -> 1 failed; A4 prefix compare skipped -> 3 failed; each mutation landed (anchor 1 -> 0, blob changed), each restore blob == HEAD (e83cb87f9db6) and git diff HEAD empty.",
"gates": "head aa57f6dd (main fdad1786 merged, no overlap with plugin-auth/service-settings/spec): dispatch-gates --commands derived 62; each run with exit captured before any pipe; --ran: '62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED'; all exit 0. Also run: check:authz-resolver, check:changeset-fixed, check:error-code-casing, check:filter-alias-parity (roster under a changed directory) and pnpm lint, all exit 0. Earlier run at d9855b3c: check-plugin-teardown-shape --self-test exit 3 on the shallow checkout, exit 0 (48 cases) after fetching 621a4876; dual-build-cjs-loads exit 3 then (no whole-closure build), exit 0 at aa57f6dd. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"line_budget": "5 files, +160 / -29 vs merge base (dispatch-gates count); under the 3000 human-merge threshold",
"files_changed": [
".changeset/30320-ipv6-cidr-allowed-ranges.md",
"packages/plugins/plugin-auth/src/auth-manager.ts",
"packages/plugins/plugin-auth/src/auth-manager.test.ts",
"packages/plugins/plugin-auth/src/allowed-ip-ranges.pin.test.ts",
"packages/plugins/plugin-auth/src/client-address-canonical.ts"
],
"deviations": [
"packages/plugins/plugin-auth/src/client-address-canonical.ts: one header sentence only (comment), outside 'ipMatchesRange and helpers': it said the allow-list compares exact IPv6 entries as text, which this change makes false.",
"Exact IPv6 entries now compare by value (spelling-insensitive) and a hex-form IPv4-mapped address folds to IPv4: both only admit clients the entry already denotes; declared Clause-②: no.",
"Manifest help text unchanged (preferred route taken; 'CIDR ranges' now holds for both families).",
"PR body footer: the session-URL line follows the session-context's PR-body block rather than the os-dev underscore footer form."
],
"mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); no write tool",
"api_writes": "2 relay writes — pr_create #30392 (fleet-write run 42000624707, 8701 bytes sent = stored), os-dev-report comment on #30320 (post-stamped); plus git pushes to the designated branch",
"open_questions": [],
"security_findings": "withheld",
"out_of_scope_findings": [
"carrier: seat (docs) · the allowed_ip_ranges help text gives only an IPv4 example (203.0.113.0/24); an IPv6 example would teach the now-working form; changing it moves the translation bundles · noted, not filed"
]
}
@objectstack/plugin-auth patch, Clause-②: no; next release
State change: pm:dispatched removed. An IPv6 CIDR in auth.allowed_ip_ranges now admits the IPv6 clients inside it at the sign-in gate and the save check.
Activity
objectstack-fleet commentedon Nov 5, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-05T07:05Z⛔ Not a claim.
auth-plugin.ts.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Claim: PM loop round 4
Session:
session_01Utp6Cbvcmv8YWFhFJwzjKL(seat) · dev threadsession_01Utp6CXwcgsAkqebfzjnfiSAccount:
os-billBranch:
claude/project-thread-g6bjfy(harness-pinned thread branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-30320Domain:
domain:serverSeat:
domain:server#2File surface:
packages/plugins/plugin-auth/src/auth-manager.ts(ipMatchesRangeand its helpers only, or a small module it imports), tests, possiblypackages/services/service-settings/src/manifests/auth.manifest.ts(help text, only on the refuse-at-save route), one changeset.changeset/30320-ipv6-cidr-allowed-ranges.md. ⛔packages/spec/**,ui/**,apps/**,skills/**,docs/adr/**,content/docs/**,.claude/**. (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default tier (opus) — dispatch-gates --tier: no path-derived mandate,effort: highClause-②: no expected — matching an IPv6 CIDR accepts clients a stored list already names; the dev re-declares
yesfor a new export,(narrowing)for a save-time refusal of an entry stored beforeMetadata-protocol: none
Responsibility:
ipMatchesRangematches IPv4 CIDR and exact addresses only, so an IPv6 CIDR entry never matches at the sign-in gate or the save check | the gate and the save check themselves (#30203, unchanged) | every deployment that allows IPv6 rangesThread-read: 6651976799
Premise: holds — read at
d1d90b70:ipMatchesRange(:1194) falls back to exact match for a non-IPv4 range; callers are:5905),isClientIpAllowed(allowed-ip-ranges.ts(:152) and the plain-HTTP eligibility list (:558)Serial constraints cleared: this seat's #30202 (PR #30298) and #30302 edit
auth-plugin.tsapplySettingsand the settings predicates; this card keeps to the matcher; pullmainbefore PR and before landing. Sibling seats: none onplugin-auth.Order: project file
server-seat2/dispatch-30320.md, delivered as the card thread's opening message.objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6CXwcgsAkqebfzjnfiS
os-dev-report
{ "issue": 30320, "status": "done", "branch": "claude/project-thread-g6bjfy", "pr": "https://github.com/objectstack-ai/objectstack/pull/30392", "session": "session_01Utp6CXwcgsAkqebfzjnfiS", "premise_still_valid": true, "summary": "Premise measured at d1d90b70: with the new pins, 6 failed | 7 passed, both doors included (the sign-in gate refused an IPv6 client inside an allowed IPv6 CIDR, and a settings save from inside it was refused). ipMatchesRange now reads both sides as the address they denote (IPv4 as uint32, other IPv6 as eight groups through the file's existing parseIpv6Literal; IPv4-mapped dotted or hex folds to IPv4; zone id dropped) and compares the first n of 128 bits for an IPv6 range; exact IPv6 entries compare by value. Families never cross. An IPv6 prefix outside 0..128 whole numbers leaves the entry matching only its base, as the old text fallback did; IPv4 entries keep their exact earlier reading, so no IPv4 list changes what it admits. Both doors call the one matcher. Node net.BlockList measured and rejected (it matches across families).", "tests": "New: 5 cases in auth-manager.test.ts (IP allow-list), 2 door cases in allowed-ip-ranges.pin.test.ts (real AuthPlugin + SettingsService + settings routes). pnpm --filter @objectstack/plugin-auth test: Test Files 222 passed (222), Tests 3925 passed | 10 skipped (3935) at d9855b3c and again at aa57f6dd after rebuilding the closure; typecheck exit 0. Ablations via scripts/ablation-replace.mjs (tests import src; no build between legs): A1 IPv4-branch family check dropped -> 1 failed; A2 IPv6-branch family check admits IPv4 -> 3 failed; A3 IPv6 prefix read with Number() -> 1 failed; A4 prefix compare skipped -> 3 failed; each mutation landed (anchor 1 -> 0, blob changed), each restore blob == HEAD (e83cb87f9db6) and git diff HEAD empty.", "gates": "head aa57f6dd (main fdad1786 merged, no overlap with plugin-auth/service-settings/spec): dispatch-gates --commands derived 62; each run with exit captured before any pipe; --ran: '62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED'; all exit 0. Also run: check:authz-resolver, check:changeset-fixed, check:error-code-casing, check:filter-alias-parity (roster under a changed directory) and pnpm lint, all exit 0. Earlier run at d9855b3c: check-plugin-teardown-shape --self-test exit 3 on the shallow checkout, exit 0 (48 cases) after fetching 621a4876; dual-build-cjs-loads exit 3 then (no whole-closure build), exit 0 at aa57f6dd. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "5 files, +160 / -29 vs merge base (dispatch-gates count); under the 3000 human-merge threshold", "files_changed": [ ".changeset/30320-ipv6-cidr-allowed-ranges.md", "packages/plugins/plugin-auth/src/auth-manager.ts", "packages/plugins/plugin-auth/src/auth-manager.test.ts", "packages/plugins/plugin-auth/src/allowed-ip-ranges.pin.test.ts", "packages/plugins/plugin-auth/src/client-address-canonical.ts" ], "deviations": [ "packages/plugins/plugin-auth/src/client-address-canonical.ts: one header sentence only (comment), outside 'ipMatchesRange and helpers': it said the allow-list compares exact IPv6 entries as text, which this change makes false.", "Exact IPv6 entries now compare by value (spelling-insensitive) and a hex-form IPv4-mapped address folds to IPv4: both only admit clients the entry already denotes; declared Clause-②: no.", "Manifest help text unchanged (preferred route taken; 'CIDR ranges' now holds for both families).", "PR body footer: the session-URL line follows the session-context's PR-body block rather than the os-dev underscore footer form." ], "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); no write tool", "api_writes": "2 relay writes — pr_create #30392 (fleet-write run 42000624707, 8701 bytes sent = stored), os-dev-report comment on #30320 (post-stamped); plus git pushes to the designated branch", "open_questions": [], "security_findings": "withheld", "out_of_scope_findings": [ "carrier: seat (docs) · the allowed_ip_ranges help text gives only an IPv4 example (203.0.113.0/24); an IPv6 example would teach the now-working form; changing it moves the translation bundles · noted, not filed" ] }Report taken 2026-11-05T09:14Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Landed · 2026-11-05T10:05Z
5a878a38, reviewed headaa57f6dd(seat ACCEPT 6654287995)TIER_DEFAULT, declaration face, seat-written in the ACCEPT (no published schema, no governed path)Fixes #30320@objectstack/plugin-authpatch,Clause-②: no; next releaseState change:
pm:dispatchedremoved. An IPv6 CIDR inauth.allowed_ip_rangesnow admits the IPv6 clients inside it at the sign-in gate and the save check.