Skip to content

Scaffold the EFS SDK monorepo#1

Open
JamesCarnley wants to merge 44 commits into
mainfrom
chore/scaffold
Open

Scaffold the EFS SDK monorepo#1
JamesCarnley wants to merge 44 commits into
mainfrom
chore/scaffold

Conversation

@JamesCarnley

Copy link
Copy Markdown
Member

Initial scaffold of the EFS SDK monorepo, set up from best-practice research + multiple expert-subagent review passes.

What's here

  • pnpm + Turborepo + Changesets workspace, two independently-versioned packages:
    • @efs/sdk — TypeScript SDK (tsup dual ESM/CJS, viem-native, vitest)
    • @efs/solidity — compile-in Solidity library (Foundry, ships .sol source)
  • ADR system mirrored from the contracts repo but lighter (MADR-style, no freeze ceremony): ADR-0001 layout, 0002 viem-only, 0003 compile-in Solidity.
  • Top-level docs (README, CONTRIBUTING, AGENTS.md), CI, scaffolded public API shapes reflecting the planning design (identity seam, static-vs-dynamic refs).

Verified

  • TS: build (dual ESM/CJS + types), typecheck, test (2 passing), lint — all green.
  • Solidity: wired for forge build/test/fmt, CI-verified.

Notes for review

  • npm scope is @efs (org needs claiming on npm before first publish; one-line revert to @efs-project if taken).
  • Method bodies are stubs (NotImplemented) — this is structure + decisions, not implementation.
  • Cross-cutting design stays in the planning vault (sdk-architecture.md); ADRs here implement slices of it.

🤖 Generated with Claude Code

JamesCarnley and others added 4 commits June 10, 2026 17:33
pnpm + Turborepo + Changesets workspace with two packages:
- @efs-project/sdk      — TypeScript SDK (tsup dual ESM/CJS, viem-native, vitest)
- @efs-project/solidity — compile-in Solidity library (Foundry, ships .sol source)

Includes the ADR system (mirrored from contracts, lighter — ADR-0001 layout,
0002 viem-only, 0003 compile-in Solidity), top-level docs (README, CONTRIBUTING,
AGENTS.md), CI, and scaffolded public API shapes reflecting the planning design
(identity seam, static-vs-dynamic refs). TS build/typecheck/test/lint verified
green; Solidity is CI-verified (forge not local).

Best-practice review applied (changeset config, publishConfig, forge-std CI
install, .nvmrc/engines).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Rename npm scope @efs-project/* -> @efs/* (scope confirmed unpublished;
  GitHub org stays efs-project). Pending `npm org create efs` before publish.
- ADR system: cut the contracts-inherited Etched/Durable/Ephemeral "permanence
  framing" section + the cargo-culted `Permanence:` template field (validation
  flagged as residual freeze-rigor); fold the one real point (published npm API
  is the only near-permanent surface) into Discipline.
- Add the missing procedural bits agents need: next-number mechanic, an
  ADR-worthiness threshold ("a choice with alternatives worth preserving"), and
  a worked boundary example (vault design -> SDK ADR slice).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Establishes the three-layer doc model so nothing duplicates: specs (how it
works now) vs ADRs (why we chose it) vs planning vault (cross-cutting design).
Seeds specs/overview.md (the SDK model at a glance) and wires it into AGENTS.md
as start-here reading.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: c8e5a19b20

ℹ️ 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 .github/workflows/ci.yml Outdated
- BLOCKER fix: .github/workflows/ci.yml still used @efs-project/sdk (the rename
  grep missed .yml) — would have failed every CI run. Now @efs/sdk.
- Add release.yml using npm Trusted Publishing (OIDC) — no stored NPM_TOKEN,
  provenance automatic. ADR-0004 records the decision (OIDC over long-lived
  token) + the one-time per-package trusted-publisher setup.
- Mark docs/api row "(later)"; bump recommended-next ADR numbers.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 7994df5334

ℹ️ 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 .github/workflows/release.yml Outdated
JamesCarnley and others added 3 commits June 10, 2026 19:55
- Solidity job failed: `forge install --no-commit` flag was removed in current
  Foundry. Clone forge-std (test-only dep) via git instead — version-stable.
- Changeset job failed: no changeset on the PR. Add an empty changeset (the
  designed no-release escape hatch) for the scaffold.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- CI: minimal `permissions: contents: read`, cancel-in-progress concurrency,
  `timeout-minutes` on all jobs, pin forge-std to v1.9.4 (no master drift).
- Release: pin global npm to @11; explicit `publishConfig.provenance: true` on
  both packages (belt-and-suspenders around the changesets/pnpm publish path).
- ADR-0004 reconciled with the explicit-provenance choice.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
forge fmt --check was the last red CI job. Reformatted EFSLib/EFSWriter to
Foundry style (verified locally with forge 1.7.1), simplified the stub
signatures to unnamed params/returns so there are no unused-variable warnings,
and removed the unreachable emit in _efsPinFile. Build + test pass locally.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: eadb898eab

ℹ️ 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 packages/sdk/src/index.ts Outdated
JamesCarnley and others added 3 commits June 10, 2026 21:25
- P1: release workflow ran `pnpm build` (turbo) which builds @efs/solidity via
  `forge build`, but never installs Foundry → release job would fail on main.
  Scope the release build to @efs/sdk only; the Solidity package publishes .sol
  source and needs no build artifact (CI compile-checks it per-PR).
- P2: remove the hardcoded `version = '0.0.0'` export — it would go stale after
  the first Changesets bump. Re-add derived from the manifest when implementing.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… deployer

The SDK ships a maintained chainId -> {addresses, schema UIDs} registry and
resolves EFS by chain (with a custom-deployment override). It deploys nothing;
integration tests fork a registry chain (anvil --fork-url) like the contracts
repo, rather than reimplementing EFS's CREATE3/proxy deploy. Reflected in the
overview spec.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- DX-7/ENG-14: lower Solidity pragma floor ^0.8.28 -> ^0.8.26 so the library
  is compilable alongside the contracts repo (pinned 0.8.26). Real compat bug.
- DX-11: fix wrong ADR pointer in index.ts (cited ADR-0004 = OIDC publishing
  for the error model; now points at "Recommended next").
- DX-10: repoint READMEs from the private planning vault to the public in-repo
  docs/specs/overview.md as the primary "how it works" reference.

Larger API-shape findings (resume/onProgress in pinFile, fetch+verify, resolvedBy,
branded UIDs, pagination iterator, deployments codegen) tracked for the beta-slice spec.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: d1e88d32fa

ℹ️ 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 .github/workflows/ci.yml
Comment thread packages/solidity/package.json
JamesCarnley and others added 4 commits June 10, 2026 22:26
First functional-slice plan for @efs/sdk: write/read real files with the API
shapes frozen correctly before publish. Grounded in the freeze-branch contracts
(contentHash now lives as a lens-scoped PROPERTY string; no chain deployed but a
local fork exists) and two review passes.

Adversarial review caught: (1) the spec/scaffold drifted to a FLAT API but the
validated design is NAMESPACED (efs.fs.write) — realigned, surfaced as Decision F;
(2) content verification is trust-relative not absolute (attacker can attest a
contentHash under their own lens) — honest semantics baked in; (3) honest click
count (~3 with metadata, not 2); richer WriteReceipt/preview; per-step idempotent
resume; pinned local-fork artifact boundary; declared the large-file/update/delete
seams. Decisions A/C/D/E/F surfaced for James.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…pping

- P1: the "Changeset present" gate would block Changesets' own Version Packages
  PR (it consumes the changeset files). Skip the gate for head_ref
  changeset-release/main so the version PR can pass and publish.
- P2: `pnpm test` / `pnpm --filter @efs/solidity test` failed on a clean checkout
  (forge-std absent, lib/ gitignored). Add a `setup` script + prebuild/pretest
  hooks that bootstrap forge-std, and DRY the CI solidity job to run the package
  scripts so CI and a contributor's local run are identical.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…odule

Decision A closed after two expert passes: contentHash is a bare SHA-256 digest
(lowercase hex, sha256sum-identical); the PROPERTY key is the algorithm tag — no
multihash/CID/keccak (its future-proofing is illusory; the IPFS-CID rationale is
false). Built it (it's pure, chain-independent):

- src/content/hash.ts: hashContent() + trust-relative verifyContent()
  (matches-author/mismatch/no-claim; no-claim != success; malformed claim guarded)
- tests against known SHA-256 vectors; exported from index.ts
- ADR-0006 + docs/specs/content-hash.md (incl. fetch-safety: lens-scoped mirrors,
  size-cap streaming, ignore transport Content-Type)
- updated beta-slice (Decision A done) + overview + ADR index (next: 0007/0008)

Verified by subagent (impl correct, vectors confirmed, cross-docs consistent: GO).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 39653f6167

ℹ️ 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 .github/workflows/release.yml Outdated
JamesCarnley and others added 3 commits June 10, 2026 23:28
changesets/action with `publish: pnpm release` would publish the unpublished
0.0.0 scaffold packages on the first no-changeset main run — premature, and the
@efs org doesn't exist yet. Drop the `publish:` input so the action only manages
the Version Packages PR and never publishes. Re-enable at launch with a one-line
`with: { publish: pnpm release }` (documented inline + in ADR-0004).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…rors

Realign to the namespaced client (Decision F): efs.fs.* / efs.lenses.* /
efs.eas.* / efs.raw.*. Built the foundational, chain-independent layers:

- eas/: viem-native EAS layer (vendored ABIs, SchemaEncoder, attest/multiAttest
  builders, UID re-derivation) — no ethers/eas-sdk (ADR-0002). 15 tests.
- lenses/: opaque Lens, lens()/identity()/resolveLens; throws MaxLensesExceeded
  rather than truncating (review S2); ENS resolution; MAX_LENSES=20.
- chain/: deployments registry + resolveDeployment + construct-time integrity
  gate (checks view/router bytecode, review S1) (ADR-0005).
- errors/: typed EfsError tree (NotImplemented/LensRequired/MaxLensesExceeded/
  SchemaMismatch/DeploymentNotFound).
- types/: branded DataUID, DataRef vs PathRef, WriteReceipt/ReadResult/EfsFile.
- fs.* verbs are NotImplemented stubs with their final signatures.

format script now runs `biome check --write` (organizes imports too, matching CI).
tsc/test(24)/build/lint all green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Integration review (GO) flagged two:
- B1/B2: fs.* stubs were async-typed but threw synchronously — `.catch()`/
  `.rejects`/`for await` would misbehave. Make them reject / throw-on-iterate so
  the async contract is locked before fs.* is implemented; tests use `.rejects`.
- S1: assertDeploymentIntegrity only checks bytecode presence — softened the
  doc to say so (sanity gate, not "is the right EFS contract"); the real trust
  gate (schema-UID match) is noted as landing with the read layer.

EAS layer, lenses, deployments, errors all verified by the review. tsc/test(24)/
build/lint green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 5e92a8d0f0

ℹ️ 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 packages/sdk/README.md Outdated
The quickstart still used the pre-F flat API (efs.read/efs.pinFile); the client
is namespaced (efs.fs.read/write/fetch). Updated to the accurate shape — read
returns a ref + resolvedBy, fetch gets verified bytes — and added a status note
that fs.* verbs are the target shape (currently NotImplemented).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 145e590697

ℹ️ 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 packages/sdk/src/eas/attest.ts Outdated
JamesCarnley and others added 2 commits June 10, 2026 23:53
…ex P2)

buildAttest/buildMultiAttest returned no tx-level `value`, so a non-zero
per-attestation resolver value would revert (no ETH sent). Compute and include
`value` on the ContractCall: buildAttest forwards data.value; buildMultiAttest
sums every entry across all requests. Added tests (26 total).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
From the 3-agent foundation review. The additive-now/breaking-later items:
- Type-level write gating: createEfsClient overloads return EfsReadClient (no
  write verbs) without a walletClient, EfsClient with one (viem's read/write
  split). Runtime WalletRequired backstop.
- EfsError -> viem-BaseError-grade: shortMessage, open-union `code`, cause,
  walk(). Added WalletRequired/CursorInvalid/PartialBatchFailure.
- Named, exported option/return types (ReadOptions/ListOptions/FetchOptions/
  WriteOptions) — no inline literals; param renamed `as` -> `lens`.
- Pagination seam: Page<T> + EfsList<T> (AsyncIterable + .page()).
- Batch seam: efs.batch() + BatchReceipt/OperationResult/WriteMechanism;
  fs.write documented as sugar over it.
- Wallet-agnostic seam (your point): EfsReader/EfsWriter aliases instead of raw
  viem types in the public config, so widening to an ethers adapter later is
  non-breaking. viem-core stays; any wallet already works via EIP-1193.
- FileStat (was Stat), branded steps[].uid, dropped root `export *`.

Deferred (additive, non-breaking later): full graph/props/lists/sorts namespace
skeleton — designed in a dedicated pass. tsc/test(27)/build/lint green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 4017c175b9

ℹ️ 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 packages/sdk/README.md Outdated
Renamed the read option `as` -> `lens` in the hardening batch; the README
quickstart still showed `{ as: ... }`. Fixed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ide)

Per the standards research (docs/specs/standards.md): the SDK's public boundary
is now the durable standard, not a library.

- createEfsClient accepts the standard form `{ provider: EIP1193Provider, chain,
  account? }` and wraps it with viem's custom() transport internally; viem clients
  `{ publicClient, walletClient? }` stay as a convenience form. Both normalize to
  viem inside. Write-gating preserved (account/walletClient presence → write client).
- Any wallet works (all are EIP-1193 providers); swapping/adding a client library
  never breaks a consumer. Dropped the EfsReader/EfsWriter placeholder aliases.
- Folded the standards into the ADRs: ADR-0009 (boundary implemented), ADR-0008
  (instantiation), ADR-0007 (error model = classifier over viem BaseError + RPC
  codes 4001/4100/4902). README quickstart leads with the provider form.

tsc/test(28)/build/lint/attw all green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 3443be823c

ℹ️ 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 packages/solidity/src/EFSWriter.sol Outdated
JamesCarnley and others added 6 commits June 11, 2026 01:15
The wrapper documented emitting EfsFilePinned but the stub just returned the
UID, so inheritors of the happy-path base would miss every pin once pinFile is
implemented. Restored capture -> emit -> return. EFSLib.pinFile still reverts so
the emit is unreachable today (harmless warning), but the correct shape is now
locked for the implementation.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Adds docs/specs/future-proofing.md synthesizing a second research sweep beyond
the core EIP table: SDK engineering (wevm stack), storage/durability, indexing
& history-expiry, security/clear-signing, gas/tx-lifecycle, L2/interop, key-
management, the Ethereum roadmap, and metadata. Three load-bearing constraints:
(1) history expires (EIP-4444) -> reads are index-first; (2) calldata costs/caps
(EIP-7623/7825) -> chunked, hash-on-chain-bytes-off-chain writes; (3) a hash is
integrity not availability -> pair fast+permanent mirrors. Extends standards.md
with the new EIPs/ERCs (7730/7623/7825/4444/7745/Multicall3/RIP-7212/etc.).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
A mid-conversation compaction caused the first synthesis to omit two agents'
output. Audited all 10 pass-2 agent transcripts against the doc and recovered:
- completeness-sweep agent (the "what are we missing" pass): ride-EAS-directly
  (no Final attestation ERC; 7512/5851/8273 off-target WATCH), ERC-8048/8049
  onchain key-value metadata as the closest PROPERTY mirror, ERC-2098 compact
  signatures, EAS-native expirationTime/revocable/refUID for revocation, abi.
  encode-not-encodePacked. New section 9 + standards.md rows.
- security agent: transaction-simulation seam (Tenderly/Blockaid -> fs.preview,
  pluggable, no baked keys).
- indexing agent: ship a reference subgraph/Envio/Ponder schema as docs.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Adversarially-verified 6-dimension review (docs/reviews/2026-06-11). Applies the
before-freeze surface locks; defers the additive/open items.

TS surface (A1-A12): DataRef gains chainId + resolvedBy (multi-chain identity +
fetch-can-verify); CallStatus + status/partialFailure/txHashes on receipts
(EIP-5792 600 partial-write); WriteEstimate usd-range; branded ContentHash;
OperationResult.error: EfsError + kind + txHash; stat() non-nullable
discriminated FileStat; TransportName union; VerificationStatus malformed-claim;
WriteMechanism 'sequential'; DirEntry for fs.list; PreviewOptions seam; drop
unexported PathRef.

Solidity (B1-B4): event EfsFilePinned -> EFSFilePinned w/ indexed path; pinFile
PinOpts overload (EAS-native expirationTime/revocable/refUID); _efsMkdir +
readAs/lens-stack parity wrappers; must-not-hash-paths + active-pin NatSpec.

Docs (C/D): reconcile sdk-architecture.md drift (efs.eas casing, SCHEMAS.REDIRECT,
lens singular, receipt field names); EAS EIP-712 domain via getDomainSeparator();
ERC-2098 TS-scoped; SSTORE2/EIP-7825 + payable-resolver notes. Reverse the
bundled-index-provider framing in future-proofing.md per review section F.

Gate: typecheck/build/test/lint green across @efs/sdk + @efs/solidity.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…tooling)

Productive work that doesn't depend on the contracts schema freeze, so the
freeze unblocks only the thin attestation glue.

Fetch/verify/mirror engine (src/mirror/, ADR-0010): zero-dep transport resolver
(ipfs/ar/https/data full; web3:// parsed, resolution seam'd), fetchVerified with
sequential gateway failover, per-attempt timeout, hard size cap, nosniff, and an
SSRF guard (v4/v6 incl. IPv4-mapped). Verify-before-trust against the bare-SHA-256
contentHash (ADR-0006); bytes returned even on mismatch, never executed.

Error classifier (ADR-0007, errors.ts): classifyError() maps viem BaseError /
ContractFunctionRevertedError + EIP-1193 (4001/4100/4200/4900/4901) + JSON-RPC
-32xxx to typed EfsError codes; idempotent; cause-preserving.

Tooling/supply-chain (future-proofing §1/§4): pinned exact dep versions (11; viem
peer left ranged), size-limit budget (6.45/8 kB), Knip dead-export check,
--ignore-scripts in CI. Test infra: mock EIP-1193 provider + createEfsClient
boundary tests, prool fork harness (env-gated). 75 tests pass, 1 skipped.

Gate green: typecheck/build/test/lint/size across @efs/sdk + @efs/solidity.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
CI caught 11 noNonNullAssertion violations in test/mirror.test.ts that a stale
turbo lint cache hid locally. Rather than weaken null-safety repo-wide (the rule
stays enforced in src/, where the mirror engine handles untrusted bytes), add a
biome override that exempts only test files, where expect(arr[0]!.x) is idiomatic.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 449c286907

ℹ️ 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 packages/sdk/src/mirror/ssrf.ts Outdated
The guard only matched the dotted tail (::ffff:127.0.0.1), but new URL()/Node
canonicalize it to the two-hextet hex form (::ffff:7f00:1), so an attacker-
chosen mirror like http://[::ffff:127.0.0.1]/ slipped past checkSsrf and could
reach loopback/private services. embeddedMappedIpv4() now decodes both the
dotted and hex tails and re-checks the embedded IPv4 against the v4 blocklist.
Regression test covers loopback/metadata/private mapped forms + a public one.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 78a6a6cc08

ℹ️ 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 packages/sdk/src/mirror/fetch.ts Outdated
fetchOne used redirect:'follow', so a public mirror could 30x to
http://127.0.0.1/... and fetch would follow it without re-running checkSsrf —
bypassing the private-host block. Switched to redirect:'manual' with a manual
hop loop that re-checks every Location against the SSRF guard (and rejects
non-http(s) schemes), capped at 5 hops. Browser opaque-redirects fall back to a
follow fetch (the browser enforces SSRF/CORS itself). Regression tests: a 30x to
loopback is blocked and never fetched; a 30x to a public host is followed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: a0c0d83143

ℹ️ 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 packages/sdk/src/mirror/ssrf.ts Outdated
Comment thread packages/sdk/src/mirror/fetch.ts
P1: URL.hostname preserves a trailing dot, so http://localhost./ and
http://metadata.google.internal./ slipped past the literal internal-host checks
even though DNS treats the FQDN form as the same host. checkSsrf now strips
trailing dot(s) from the host (and allowlist entries) before any comparison.

P2: the inline data: branch returned decoded bytes without checking maxBytes, so
a giant data: URI from untrusted metadata bypassed the size cap. fetchVerified
now rejects inline payloads over maxBytes (recorded as an attempt, fails over).

Regression tests for both.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 59253c1cf7

ℹ️ 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 packages/sdk/src/mirror/fetch.ts Outdated
…e bomb

Proactive adversarial hardening pass on the mirror module (the SDK's untrusted-
input boundary), from a 6-category red-team enumeration validated against actual
Node 20/undici behavior — getting ahead of the SSRF/size bypass classes rather
than fielding them one at a time.

- IPv6 SSRF: replaced the ::ffff:-only regex with a byte-level IPv6 expander +
  prefix checks. Now blocks IPv4-compatible (::7f00:1), IPv4-translated
  (::ffff:0:..), NAT64 (64:ff9b::/96), 6to4 (2002::/16 embedded v4), plus
  site-local (fec0::/10) and Teredo (2001::/32). One canonical byte check covers
  all textual variants (compressed/expanded/case/leading-zero).
- Decompression bomb: fetchOne now sends accept-encoding: identity and rejects
  any non-identity Content-Encoding BEFORE reading the body, so undici never
  pumps a gzip/br/zstd inflate (also neutralizes the chained-encoding
  CVE-2026-22036 in undici 7.16 regardless of runtime version).
- data: decode bomb: resolveData rejects by *encoded* length before decoding, so
  an oversized data: URI can't force a large allocation pre-cap.
- Confirmed-safe (lock-in tests, no code change): alternate IPv4 literal
  encodings (octal/decimal/hex/dword/part-collapse) — Node's URL parser
  canonicalizes them before checkSsrf runs.

Known gap still open: DNS rebinding (public name -> private A record) needs
connect-time IP pinning (a Node/undici dispatcher) — its own deliberate pass.
Tests: 84 (36 mirror), gate green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 2fa8bf9f9e

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
…s (P2)

ipfs://cid/%2e%2e/admin (and the arweave equivalent) string-concatenated into
new URL, which normalizes .. AND %2e%2e (WHATWG decodes percent-encoded dots
during dot-segment removal), escaping /ipfs/<cid>/ to an arbitrary path on the
trusted gateway. Now: validate CID (alphanumeric) / Arweave txid (base64url)
shape at parse, and buildGatewayUrl asserts the post-normalization pathname
still begins with the content-address namespace, rejecting otherwise. Legit
subpaths preserved. Regression tests for literal + %2e-encoded traversal.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: db195ad73a

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
JamesCarnley and others added 3 commits June 11, 2026 03:26
The startsWith(nsPath) guard from db195ad accepted a sibling sharing the CID
prefix: ipfs://bafy/../bafyadmin normalizes to /ipfs/bafyadmin, which
startsWith /ipfs/bafy. Now require pathname === nsPath or startsWith nsPath+'/'
so only the exact namespace (or a child under it) is allowed. Regression test
added.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…s (ADR-0011)

Absorbs two merged contracts features as additive surface; fs.* bodies stay
NotImplemented stubs pending the schema freeze, shapes locked now.

On-chain directory filter (contracts ADR-0048):
- ListOptions gains optional excludes (def-UID or human label) + minWeights;
  non-empty routes fs.list to the filtered view, empty = unfiltered.
- Vendor the EFSFileView view ABI (first view-layer ABI; was EAS-only):
  getDirectoryPageFiltered + unfiltered siblings, transcribed from
  deployedContracts.ts.
- Pure read helpers (src/reads/directory.ts): shouldUseFilteredQuery,
  reconcileMinWeights (all-zero default avoids the length-mismatch revert),
  validateDirectoryQuery (fails fast on the 20-attester / 8-exclude / maxItems
  caps) + InvalidDirectoryQuery. 18 tests.
- No default excludes; SAFETY_EXCLUDES=['system','nsfw'] exported for opt-in.

Folder Overviews:
- fs.overview(path) -> discriminated OverviewResult (none|markdown|binary|
  too-large), exact-path resolve; fs.setOverview(container, markdown) (system
  TAG before placement). Convention constants OVERVIEW_NAME, MAX_RENDER_BYTES.
  No new schema/contract/reserved key.

Deployments reconciled to contracts source-of-truth: drop phantom redirect
schema + aliasResolver contract; add real blob/sortInfo/naming schemas +
fileView/edgeResolver/sortOverlay/schemaNameIndex/listReader contracts. Add
'InvalidArgument' error code. Fix stale ADR-0011->ADR-0010 transport refs.

Gate green: lint/typecheck/test(102)/build across @efs/sdk + @efs/solidity.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… surface

The ADR-0011 additions (first view-layer ABI + directory-filter helpers +
Overview types) grew the gzipped ESM bundle from 6.45 -> 8.15 kB, past the 8 kB
budget. size-limit guards regressions, not deliberate surface growth — raise to
10 kB (~25% headroom per our bundle doctrine). Revisit subpath-splitting the
ABIs if bundle size later becomes a real constraint.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 310ef5f138

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
Comment thread packages/sdk/src/errors.ts
…s (P2 x2)

P2 (transport): the data: text-path size guard measured UTF-16 string length, so
a payload within the char cap could decode past it (€ = 1 char, 3 UTF-8 bytes),
and resolveTransport used directly never enforced the cap. Now: pre-decode reject
on a cheap lower bound (ceil(len/3) for text) to bound transient allocation, plus
an authoritative exact UTF-8 byteLength cap inside resolveData itself.

P2 (errors): classifyError read the numeric EIP-1193/1474 code off the OUTER
error only, so a 4001/-32xxx wrapped under a viem contract/tx error fell through
to generic. numericCode now walks the cause chain (cycle-guarded).

Tests: UTF-8 vs UTF-16 data: cap (encoded + literal non-ASCII), nested-cause
4001->UserRejected and -32000->RpcError. Gate incl. size: 104 tests, green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: 8481394982

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
…k (P2)

The base64 pre-decode bound counted `=` padding (and whitespace), over-estimating
by up to 2 bytes — so an at-cap payload like data:;base64,aGVsbG8= (5 bytes) was
falsely rejected at maxBytes=5 before the authoritative byteLength check. Strip
whitespace + trailing padding first: floor(sig*3/4). Regression test: at-cap
padded payload is now accepted.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: f4f9bb5564

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
Comment thread packages/sdk/src/eas/abi.ts Outdated
…P2 x2)

P2 (transport): non-base64 data: payloads are octets (RFC 2397), but
decodeURIComponent treats %XX as UTF-8 and throws on binary like
data:application/octet-stream,%ff. New decodeDataOctets decodes %XX byte-wise
(literal runs as UTF-8), so binary inline mirrors hash instead of failing.

P2 (eas): easAbi exported only function fragments, so viem couldn't populate
ContractFunctionRevertedError.data.errorName on a real attest/multiAttest revert
and the classifier's InvalidSchema->SchemaMismatch mapping never fired. Added the
22 EAS custom-error fragments (transcribed from eas-contracts EAS.sol/IEAS.sol).

Tests: binary octet decode (%ff, mixed literal+octet); InvalidSchema revert
decoded against the real easAbi -> SchemaMismatch. Gate incl. size: 107 tests.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@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: fa56b1134d

ℹ️ 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 packages/sdk/src/mirror/transport.ts Outdated
WHATWG data: processing percent-decodes the body before base64-decoding, so a
producer may percent-encode base64 specials (data:...;base64,%2Fw%3D%3D == 0xff).
We passed raw %XX to atob (throws) and the size estimate counted the triplets
(false oversize at small caps). Now decodeURIComponent the base64 body first
(ASCII; falls back to raw on malformed input), then estimate + decode. Regression
test: %2Fw%3D%3D -> 0xff, incl. at maxBytes=1.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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