Skip to content

feat(auth): add OpenWallet Standard wallet support - #458

Open
SgtPooki wants to merge 11 commits into
masterfrom
spike/ows-wallet
Open

feat(auth): add OpenWallet Standard wallet support#458
SgtPooki wants to merge 11 commits into
masterfrom
spike/ows-wallet

Conversation

@SgtPooki

@SgtPooki SgtPooki commented May 6, 2026

Copy link
Copy Markdown
Collaborator

What changed

Wires the OpenWallet Standard (openwallet.sh) viem adapter into filecoin-pin's existing AccountConfig path. Users can now authenticate via an OWS-managed wallet, and the private key never leaves the OWS native core.

  • New src/core/ows/index.ts builds a viem Account from an OWS wallet ID via @open-wallet-standard/adapters/viem.
  • --wallet <id> / OWS_WALLET_ID and --wallet-passphrase / OWS_WALLET_PASSPHRASE flags added to CLI commands using the shared auth options (add, import, payments, data-set, provider, rm). The server daemon and session commands still require PRIVATE_KEY; OWS support there is follow-up work.
  • parseCLIAuth() resolver: --wallet takes precedence over --private-key / PRIVATE_KEY. Devnet auto-resolve preserved.
  • Synapse SDK is already viem-native (AccountConfig branch in initializeSynapse()), so no SDK plumbing changes are required.

Status

Ready for review. This started as a spike and is now opened for review and collaboration. The read path is validated end-to-end on Calibration (see Verification below). Real signing against synapse-sdk still needs to be exercised before merge.

The Haven team (@Apha205, @HavenCTO) raised this on #457 and offered to validate OWS for their use case, resolve conflicts, and exercise real signing on Calibration. Reviewers are flagged so they can test against their setup.

Before merge: a funded add upload or payments setup against Calibration, to confirm the OWS-backed Account signs correctly end-to-end (the read path is already validated below).

How to verify

  1. Install OWS: npm i -g @open-wallet-standard/core
  2. Create or import a wallet:
    • New: ows wallet create --name fil-test
    • Or import an existing EVM key: echo "$YOUR_TESTNET_KEY" | ows wallet import --name fil-test --private-key --chain ethereum
  3. Run: OWS_WALLET_ID=fil-test pnpm exec tsx src/cli.ts payments status --network calibration
  4. Confirm Synapse initializes and balances are returned without PRIVATE_KEY set.

Notes / risks

  • Platform coverage: OWS prebuilt binaries published for linux-x64-gnu, linux-arm64-gnu, darwin-x64, darwin-arm64. No Windows or musl/Alpine target on npm today. Windows and Alpine users continue to use PRIVATE_KEY.
  • @open-wallet-standard/core is Node-only (CJS, uses fs / child_process). Browser exports are unaffected, OWS only enters the CLI / server / Action paths.

Verification

Validated end-to-end on Calibration with an OWS-managed wallet imported from an existing testnet private key:

$ unset PRIVATE_KEY
$ OWS_WALLET_ID=fil-test pnpm exec tsx src/cli.ts payments status --network calibration

┌  Filecoin Onchain Cloud Payment Status
│
◇  ✓ Configuration loaded
◇  ✓ Data sets fetched
◇  ⚠ Actual storage calculated with N warning(s)
│
│  ━━━ Current Status ━━━
│  Wallet
│    Owner address: 0x<redacted>
│    Network: Filecoin - Calibration testnet
│    FIL: <redacted> tFIL
│    USDFC: <redacted> USDFC
│
│  Filecoin Pay
│    Balance: <redacted> USDFC
│    Locked: <redacted> USDFC (30-day reserve)
│    Available: <redacted> USDFC
│    ...
│
│  WarmStorage Usage
│    Stored: <redacted>
│    Runway: <redacted>
│    ...
│
└  Status check complete

Confirmed:

  • Synapse SDK initialized through the OWS-backed viem Account (no SDK plumbing changes).
  • parseCLIAuth() resolver picked --wallet / OWS_WALLET_ID over PRIVATE_KEY / dotenv-loaded keys.
  • Owner address derived by OWS matches the address derived from the same private key by viem's privateKeyToAccount, so existing balances and data sets resolve unchanged.
  • "Failed to fetch provider-side pieces" warnings are unrelated to auth (provider-side reporting).

Behavior notes

  • Uses the Ethereum CAIP-2 namespace, not native Filecoin. filecoin-pin signs via FEVM through synapse-sdk's viem path, so getOwsAccount() always asks OWS for an eip155:<chainId> account (eip155:314 mainnet, eip155:314159 Calibration). OWS wallets typically also expose a native Filecoin entry (fil:mainnet → f1...) derived from the same seed, and that one is not used. When you run ows wallet list you'll see both; the 0x... EVM address is the one filecoin-pin authenticates with.
  • The OWS viem adapter falls back to any eip155:* account on the wallet when the requested chain isn't explicitly registered (wallet.accounts.find((a) => a.chainId.startsWith("eip155:"))). OWS derives one secp256k1 key shared across all EVM chains, so the same address resolves on Calibration even when the wallet only lists eip155:1. Our chain: eip155:<id> parameter is best-effort.
  • getOwsAccount() defensively rejects any account whose address isn't a 40-char EVM address, in case a future adapter version returns a non-eip155 account.
  • Real signing (a funded add upload or payments setup) was not exercised in this run; only the read path through Synapse.create + balance/data-set queries.

Refs #457

Closes #457

Wires @open-wallet-standard/adapters/viem into the existing AccountConfig
path so signing can be delegated to OWS (private key never leaves the OWS
core).

Adds:
- --wallet <id> / OWS_WALLET_ID
- --wallet-passphrase / OWS_WALLET_PASSPHRASE

Resolver order: --wallet (OWS) takes precedence over --private-key /
PRIVATE_KEY. devnet auto-resolve still applies when no auth supplied.

Refs #457
@FilOzzy FilOzzy added team/filecoin-pin "Filecoin Pin" project is a stakeholder for this work. team/fs-wg FOC working group is a stakeholder for this work, and thus wants to track it on their project board. labels May 6, 2026
@FilOzzy FilOzzy added this to FOC May 6, 2026
@github-project-automation github-project-automation Bot moved this to 📌 Triage in FOC May 6, 2026
@SgtPooki SgtPooki self-assigned this May 6, 2026
…port

@open-wallet-standard/core ships napi-rs prebuilt binaries only for
linux-x64-gnu, linux-arm64-gnu, darwin-x64, and darwin-arm64. A static
import of @open-wallet-standard/adapters/viem triggers a require of the
native binding at module evaluation time, crashing CLI startup on Windows
runners (and any platform without a published prebuilt) even when the
user never asks for OWS auth.

Load the adapter via ESM dynamic `import()` inside getOwsAccount() so the
native binding only resolves when --wallet / OWS_WALLET_ID is actually
used. Surface a helpful error if the platform lacks a prebuilt.

Makes getOwsAccount() and parseCLIAuth() async; updates the 12 call sites
(already inside async action handlers) to await.

Refs #457
@SgtPooki
SgtPooki force-pushed the spike/ows-wallet branch from 63c6524 to c4e35e2 Compare May 6, 2026 20:00
OWS wallets typically expose both an eip155:* (EVM) account and a
fil:* (native Filecoin) account derived from the same seed. filecoin-pin
signs via FEVM through synapse-sdk and only uses the eip155 entry; the
fil:* / f1 address is not used. Users seeing both in `ows wallet list`
could reasonably assume the native one is the right pick.

Add a defensive check that rejects any non-EVM address returned by the
adapter, with an error message pointing at `ows wallet list`. Also
document the FEVM-only scope in the module doc comment.

Refs #457
@BigLep BigLep moved this from 📌 Triage to ⌨️ In Progress in FOC May 7, 2026
@BigLep BigLep moved this from ⌨️ In Progress to 🐱 Todo in FOC May 12, 2026
@BigLep BigLep added this to the M4.5: GA Fast Follows milestone May 25, 2026
@SgtPooki SgtPooki changed the title feat(auth): spike OpenWallet Standard wallet support feat(auth): add OpenWallet Standard wallet support Jun 29, 2026
@SgtPooki
SgtPooki marked this pull request as ready for review June 29, 2026 22:16
@SgtPooki
SgtPooki requested review from BravoNatalie and Copilot June 29, 2026 22:16

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.

Pull request overview

This PR adds OpenWallet Standard (OWS) wallet authentication to filecoin-pin’s CLI auth resolver, allowing users to supply an OWS wallet ID (and optional passphrase) so signing can be performed by an OWS-managed viem Account instead of exposing raw private keys.

Changes:

  • Added --wallet/OWS_WALLET_ID and --wallet-passphrase/OWS_WALLET_PASSPHRASE CLI options and wired them into the shared auth path.
  • Introduced src/core/ows/index.ts to lazily load the OWS viem adapter and produce a viem Account.
  • Updated CLI flows to await parseCLIAuth() after making it async to support OWS account resolution.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/utils/cli-options.ts Adds new Commander options for OWS wallet ID + passphrase.
src/utils/cli-auth.ts Makes auth parsing async; adds OWS account resolution and precedence.
src/core/ows/index.ts New OWS integration module that builds a viem Account via dynamic import.
src/core/synapse/index.ts Updates missing-auth error message to mention OWS wallet auth.
src/add/add.ts Awaits async auth parsing before Synapse init.
src/import/import.ts Awaits async auth parsing before Synapse init.
src/payments/auto.ts Awaits async auth parsing before Synapse init.
src/payments/deposit.ts Awaits async auth parsing before Synapse init.
src/payments/fund.ts Awaits async auth parsing before Synapse init.
src/payments/status.ts Awaits async auth parsing before Synapse init.
src/payments/withdraw.ts Awaits async auth parsing before Synapse init.
src/rm/remove-all-pieces.ts Awaits async auth parsing before Synapse init.
src/rm/remove-piece.ts Awaits async auth parsing before Synapse init.
package.json Adds OWS core + adapter dependencies.
pnpm-lock.yaml Locks OWS dependencies and platform-specific optional packages.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/cli-auth.ts Outdated
@SgtPooki

Copy link
Copy Markdown
Collaborator Author

@HavenCTO @Apha205, updated the PR.. feel free to leave feedback

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.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/utils/cli-auth.ts Outdated
Comment thread src/utils/cli-auth.ts Outdated
Comment thread src/utils/cli-options.ts
Comment thread src/utils/cli-options.ts
SgtPooki added 2 commits July 22, 2026 12:44
When --rpc-url is set, probe the endpoint's chainId before requesting
the OWS account so the eip155 hint matches the target RPC, instead of
defaulting to calibration. Treat an empty OWS_WALLET_PASSPHRASE as not
provided.
@SgtPooki

Copy link
Copy Markdown
Collaborator Author

ping @hugomrdias @Apha205 @BravoNatalie

Comment thread package.json Outdated
Comment thread src/utils/cli-auth.ts Outdated
Comment thread src/utils/cli-auth.ts
* @returns Synapse setup config (validation happens in initializeSynapse)
*/
export function parseCLIAuth(options: CLIAuthOptions): SynapseSetupConfig {
export async function parseCLIAuth(options: CLIAuthOptions): Promise<SynapseSetupConfig> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

continuing on my precedence suggestion below, the complexity in here now deserves a bunch of tests to show it does what you expect, and document what you expect too

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

preferences should be much more clear now

@rvagg

rvagg commented Jul 23, 2026

Copy link
Copy Markdown
Member

@SgtPooki did your testing extend beyond the payments command into actual data onboarding? This JSON.stringify looks like a problem for our bigints in 712 payloads, does it get past this? https://github.com/open-wallet-standard/core/blob/76c8c67d6c73bf57805432fc15a2e48d12168c6c/bindings/node-adapters/src/viem.js#L36

@SgtPooki

Copy link
Copy Markdown
Collaborator Author

@SgtPooki did your testing extend beyond the payments command into actual data onboarding? This JSON.stringify looks like a problem for our bigints in 712 payloads, does it get past this? open-wallet-standard/core@76c8c67/bindings/node-adapters/src/viem.js#L36

I didn't do super thorough testing, but I have now. I fixed this in our implementation here, added unit tests, and also opened an upstream fix: github.com/open-wallet-standard/core#242

SgtPooki added 4 commits July 27, 2026 14:11
The OWS viem adapter serializes typed data with JSON.stringify, which
throws on the bigints every synapse-sdk EIP-712 payload carries
(clientDataSetId, nonce, pieceIndex, permit value/deadline). Override
signTypedData to serialize the message the way the OWS core accepts:
bigints as even-length hex, with EIP712Domain injected when absent.

signMessage and signTransaction are left to the adapter.

Validated byte-identical to viem's privateKeyToAccount, and confirmed
on-chain (AddPieces, SchedulePieceRemovals) on Calibration.
Auth options merged from CLI flags and env vars could let an env var
silently beat an explicit flag. Capture each flag's source (cli vs env)
in a preAction hook and resolve precedence in parseCLIAuth: an explicit
flag wins over env, and two modes from the same tier is a hard error.

Session-key mode competes only when both halves are present, so a lone
--wallet-address or --session-key no longer outranks a complete mode.
Interactive setup prompted for a private key whenever one was not passed,
then handed it to parseCLIAuth. With an OWS wallet supplied that tripped
the mutual-exclusion check. Skip the prompt only when a sign-capable mode
is present: an OWS wallet or a complete session key. A view address and a
lone session-key half cannot run setup, so they still prompt.
Bumps @open-wallet-standard/core and /adapters from 1.3.2 to 1.4.2,
the release that adds trusted publishing.

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.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (3)

src/utils/cli-auth.ts:187

  • OWS mode precedence is determined only from the wallet option source (const owsSource = sourceOf('wallet', ...)). If OWS_WALLET_ID is set via env but the user passes --wallet-passphrase explicitly to disambiguate from an env PRIVATE_KEY, parseCLIAuth() will still treat both modes as env-sourced and throw a conflict. This also contradicts the comment that a mode spanning several options should take the strongest source (like the session-key mode does). Consider tracking walletPassphrase in AuthOptionSources and using strongest(walletSource, walletPassphraseSource) when computing the OWS candidate source.
  const walletAddressSource = sourceOf('walletAddress', walletAddress)
  const sessionKeySource = sourceOf('sessionKey', sessionKey)
  const sessionSource =
    walletAddressSource && sessionKeySource ? strongest(walletAddressSource, sessionKeySource) : undefined
  if (sessionSource)
    candidates.push({ mode: 'sessionKey', source: sessionSource, label: '--wallet-address/--session-key' })
  const owsSource = sourceOf('wallet', owsWalletId)
  if (owsSource) candidates.push({ mode: 'ows', source: owsSource, label: '--wallet/OWS_WALLET_ID' })

src/utils/cli-options.ts:20

  • collectAuthOptionSources() tracks provenance for mutually exclusive auth options, but AUTH_OPTION_NAMES currently omits walletPassphrase. If a user relies on env OWS_WALLET_ID and provides --wallet-passphrase explicitly (or vice versa), parseCLIAuth can't treat the OWS mode as explicitly selected, which can cause avoidable "Conflicting authentication options" errors when other env auth (e.g. PRIVATE_KEY) is also set. Include walletPassphrase in the collected option names (and in AuthOptionSources) so OWS mode can apply the same "strongest source wins" rule as session-key mode.
 * Commander attribute names for the mutually exclusive auth flags whose
 * provenance {@link parseCLIAuth} needs to resolve precedence.
 */
const AUTH_OPTION_NAMES = ['privateKey', 'wallet', 'walletAddress', 'sessionKey', 'viewAddress'] as const

src/utils/cli-auth.ts:123

  • The conflict error text says "Provide exactly one signing mode", but the candidate set includes read-only mode (--view-address), which is explicitly non-signing. This can be confusing when users hit a conflict involving view-only auth. Consider wording this as "authentication mode" (or similar) instead.
  const conflict = (modes: AuthModeCandidate[], envOnly: boolean): Error => {
    const labels = modes.map((c) => c.label).join(' and ')
    const hint = envOnly ? ' Pass an explicit flag to disambiguate.' : ''
    return new Error(`Conflicting authentication options: ${labels}. Provide exactly one signing mode.${hint}`)
  }

@SgtPooki
SgtPooki requested review from Copilot and rvagg and removed request for Copilot July 28, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team/filecoin-pin "Filecoin Pin" project is a stakeholder for this work. team/fs-wg FOC working group is a stakeholder for this work, and thus wants to track it on their project board.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support keystore-based auth so users do not paste raw private keys

6 participants