From 99d4c0c2be1ed9f6de2ce0d3bb2c0070423dfd93 Mon Sep 17 00:00:00 2001 From: JAG-UK Date: Sun, 2 Aug 2026 15:31:58 +0100 Subject: [PATCH 1/4] feat: programmable ACLs docs First cut at dev docs --- docs/astro.config.mjs | 2 + .../docs/core-concepts/fwss-overview.mdx | 21 +- .../developer-guides/access-control/_meta.yml | 3 + .../access-control/programmable-acls.mdx | 251 ++++++++++++++++++ .../{ => access-control}/session-keys.mdx | 16 +- .../content/docs/developer-guides/index.md | 3 +- .../content/docs/getting-started/index.mdx | 3 +- 7 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 docs/src/content/docs/developer-guides/access-control/_meta.yml create mode 100644 docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx rename docs/src/content/docs/developer-guides/{ => access-control}/session-keys.mdx (94%) diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 40e52177a..50b69e966 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -21,6 +21,8 @@ export default defineConfig({ '/developer-guides/storage/split-operations/': '/developer-guides/storage/upload-pipeline/', '/developer-guides/react-integration/': '/developer-guides/synapse-react/', '/developer-guides/devnet/': '/resources/devnet/', + '/developer-guides/session-keys/': '/developer-guides/access-control/session-keys/', + '/developer-guides/programmable-acls/': '/developer-guides/access-control/programmable-acls/', }, markdown: { // rehype-external-links attaches to the unified processor Starlight runs. diff --git a/docs/src/content/docs/core-concepts/fwss-overview.mdx b/docs/src/content/docs/core-concepts/fwss-overview.mdx index 18876200c..c435edf4f 100644 --- a/docs/src/content/docs/core-concepts/fwss-overview.mdx +++ b/docs/src/content/docs/core-concepts/fwss-overview.mdx @@ -28,12 +28,31 @@ Together, FWSS enables builders to depend on Filecoin not only for “store and WarmStorage manages the complete storage marketplace: -1. **Client Authentication**: Validates all client operations via EIP-712 signaturess. +1. **Client Authentication**: Validates all client operations via EIP-712 signatures. 2. **Payment Coordination**: Automatically creates and manages payment rails between clients and service providers. 3. **Cost Calculation**: Determines pricing based on size, duration, and CDN usage. 4. **Metadata Management**: Stores data set and piece metadata for discovery. 5. **Fault Handling**: Integrates PDP verification results with payment adjustments +### Authorizing writes + +For the write operations on a data set — adding pieces, scheduling piece removals, and terminating +the service — FWSS supports three authorization models, in increasing order of flexibility: + +1. **Payer signature (default)**: the data set's payer signs an EIP-712 message; FWSS recovers the + secp256k1 signer and checks it against the payer. +2. **[Session keys](/developer-guides/access-control/session-keys/)**: the payer delegates to an ephemeral + secp256k1 key with time-limited, per-operation permissions recorded in the `SessionKeyRegistry`. + This is the recommended way to get silent, popup-free signing while keeping the standard model. +3. **[Programmable ACLs](/developer-guides/access-control/programmable-acls/) (Data Set Authorizers)**: the payer + attaches a contract implementing `IDataSetAuthorizer` to a single data set. FWSS then delegates + the entire authorization decision for that data set's writes to the contract, which can enforce + any policy — verifying a **different curve** (e.g. a P256 passkey / WebAuthn assertion), requiring + human presence, or adding expiry, rate-limits, or a kill-switch. + +The three are complementary: session keys and programmable ACLs are both opt-in delegation layers on +top of the default payer-signature model, chosen per data set. + ## How FWSS works **Filecoin Warm Storage Service (FWSS)** combines PDP (Proof of Data Possession) verification with integrated payment rails using Filecoin Pay to offer data set management for developers. diff --git a/docs/src/content/docs/developer-guides/access-control/_meta.yml b/docs/src/content/docs/developer-guides/access-control/_meta.yml new file mode 100644 index 000000000..bff0601a6 --- /dev/null +++ b/docs/src/content/docs/developer-guides/access-control/_meta.yml @@ -0,0 +1,3 @@ +label: Access Control +collapsed: true +order: 6 diff --git a/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx new file mode 100644 index 000000000..2a3598e1b --- /dev/null +++ b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx @@ -0,0 +1,251 @@ +--- +title: Programmable ACLs +description: Delegate the entire write-authorization decision for a data set to your own on-chain contract (Data Set Authorizers). +sidebar: + order: 2 +--- + +:::tip[Alternative: session keys] +Programmable ACLS in FWSS provide rich, fine-grained per-dataset, per-operation control using a +smart contract that you supply. They enable you to use different signing algorithms and more detailed +per-dataset delegation (eg **P256 passkey** (Touch ID) a multisig, or per-operation rate-limits) than +the default mechanism. + +However where such fine control is not necessary, [session keys](/developer-guides/access-control/session-keys/) +are the recommended default for silent, popup-free signing with the standard secp256k algorithm. They are fast and +cheap and work with raw Synapse SDK as well as FWSS. + +The two are complementary and can be used together on different datasets belonging to the same owner. See +[Which should I use?](#session-keys-vs-programmable-acls) for more details. +::: + +## What are programmable ACLs? + +By default, every write to a data set — adding pieces, scheduling piece removals, terminating the +service — is authorized by an **EIP-712 signature from the data set's payer**, optionally delegated +to a [session key](/developer-guides/access-control/session-keys/). Session key delegation offers a +powerful UX upgrade over repetitive wallet signing operations but they delegate authority for *all +datasets* that the payer owns. + +A **programmable ACL** overrides that built-in auth check on an individual dataset basis with a +smart contract supplied by the payer that implements the `IDataSetAuthorizer` interface. +When the payer attaches an *authorizer* to a dataset, FWSS calls the authorizer to make the +decision **instead of** checking the session key registry. + +The authorizer can implement any policy you want: + +- verify a signature using a different algorithm — for example a **P256 passkey** assertion + (Touch ID, secure enclave), which the built-in secp256k1 path cannot do; +- require **human presence** (a biometric user-verification flag) for sensitive operations; +- gate on the operation's contents (metadata, piece paths); +- authorize a machine agent's stored key **and** your human passkey on the same data set, + each scoped to different operations. + +## The interface + +An authorizer is any contract implementing a single method: + +```solidity +interface IDataSetAuthorizer { + function isAuthorized( + uint256 dataSetId, + address payer, + bytes32 operation, // EIP-712 type hash of the op (AddPieces / SchedulePieceRemovals / TerminateService) + bytes32 digest, // the EIP-712 digest FWSS computed for this exact operation + bytes calldata signature, // opaque to FWSS — your contract interprets it + bytes calldata operationData // ABI-encoded raw op payload (empty for terminate) + ) external returns (bool authorized); +} +``` + +FWSS calls this **instead of** its built-in signature check, for the three write operations. +Semantics: + +- return **`true`** → the operation proceeds; +- return **`false`** → FWSS reverts with `Unauthorized`; +- **revert / out-of-gas** → treated as "not authorized"; the operation reverts. + +`isAuthorized` is a **state-mutating** call (not `view`), so an authorizer may update its own storage +while deciding — consume a nonce, tick a rate-limiter, log an event. FWSS gas-caps the sub-call and +blocks re-entry into the authorization path while a decision is in flight. + +The `digest` is FWSS's EIP-712 digest for that exact operation and its parameters; the `signature` +is whatever blob your authorizer expects (FWSS does not interpret it); `operationData` is the raw +ABI-encoded operation payload, so a policy can gate on contents (it is empty for terminate). + +:::caution[The authorizer is the *sole* gate for the operations it covers] +While attached, the authorizer **fully replaces** the session-key signature check for +add-pieces, schedule-removals, and *signed (immediate)* termination. FWSS does no signature recovery +of its own for those — your contract owns the whole decision, **including verifying whatever +`signature` it expects over `digest`**. Exercise extreme diligence in security review of any +authorizer contract you deploy. Ideally you should use an FWSS-provided contract and only write +your own if you need functionality that is not already covered. +::: + +:::caution[Gas limits] +In order to mitigate SP griefing the entire `isAuthorized` check is hard limited to 150M gas. +This is enough to implement something sophisticated like a touchID passkey verifier but you +still need to be careful an write optimized code when writing or deploying your own authorizer. +::: + +## Step 1 — write and deploy an authorizer + +Here is a minimal authorizer that accepts a **P256 (secp256r1) signature** over the operation +digest from a single registered key — the kind of delegation the built-in secp256k1 path can't +express. It verifies via the FEVM **secp256r1 precompile at `0x100`**: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract SingleP256Authorizer { + uint256 public immutable pubKeyX; + uint256 public immutable pubKeyY; + + constructor(uint256 x, uint256 y) { pubKeyX = x; pubKeyY = y; } + + function isAuthorized(uint256, address, bytes32, bytes32 digest, bytes calldata signature, bytes calldata) + external view returns (bool) + { + (bytes32 r, bytes32 s) = abi.decode(signature, (bytes32, bytes32)); + // 0x100 input: digest ‖ r ‖ s ‖ x ‖ y ; returns 32-byte 1 on success + (bool ok, bytes memory out) = + address(0x100).staticcall(abi.encodePacked(digest, r, s, bytes32(pubKeyX), bytes32(pubKeyY))); + return ok && out.length == 32 && bytes32(out) == bytes32(uint256(1)); + } +} +``` + +Deploy it with your usual tooling (Foundry, Hardhat, or `viem`'s `deployContract`), passing your +P256 public-key coordinates to the constructor. + +## Step 2 — attach it to a data set + +*** JAGTAG *** See if we can update the ABI to prevent this being necessary. + +Attaching is a call to `setDataSetAuthorizer(dataSetId, authorizer)` on the FWSS contract. **Only the +data set's payer may call it.** Because the SDK's bundled FWSS ABI predates this method, use an +inline ABI fragment with `viem`: + +```ts +import { createWalletClient, http, getAddress, type Hex } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { calibration } from '@filoz/synapse-core/chains' + +// FWSS address for your network — see /resources/contracts/ +const WARM_STORAGE = '0x...' as Hex + +const setAuthorizerAbi = [{ + type: 'function', name: 'setDataSetAuthorizer', stateMutability: 'nonpayable', + inputs: [{ name: 'dataSetId', type: 'uint256' }, { name: 'authorizer', type: 'address' }], + outputs: [], +}] as const + +const payer = createWalletClient({ + account: privateKeyToAccount('0x' as Hex), + chain: calibration, + transport: http(), +}) + +await payer.writeContract({ + address: WARM_STORAGE, + abi: setAuthorizerAbi, + functionName: 'setDataSetAuthorizer', + args: [42n /* dataSetId */, getAddress('0x')], +}) +``` + +`setDataSetAuthorizer` requires the target to be a deployed contract (`authorizer.code.length > 0`), +or `address(0)` to detach (see Step 4). You can read the current authorizer back with the matching +`getDataSetAuthorizer(uint256) → address` view (or `getDataSetAuthorizer` on the FWSS **State View** +contract). + +## Step 3 — authorize operations with `extraData` + +:::caution[Total delegation of all operations] +Once an authorizer is attached, FWSS delegates all access control decisions to it and the session +key registry is not checked. You must therefore include checks for all operations in the authorizer +(or make calls from the payer address, which will always succeed). + +FWSS operations that can be gated by the authorizer are: + - AddPieces + - SchedulePieceRemovals + - TerminateService + +Build the blob your `isAuthorized` implementation expects and pass it as the pre-built +**`extraData`** on the operation: + +```ts +import { encodeAbiParameters, type Hex } from 'viem' + +// For our SingleP256Authorizer, `signature` = abi.encode(r, s) over the FWSS digest. +// Compute the digest FWSS will use for this add-pieces operation, sign it with your P256 key, +// then wrap the (r, s) exactly as your authorizer's abi.decode expects. +const signature = encodeAbiParameters( + [{ type: 'bytes32' }, { type: 'bytes32' }], + [rHex, sHex], +) +// FWSS forwards `extraData` to the SP, which passes it to your authorizer as `signature`. +const extraData = signature as Hex + +// High-level: pass extraData to skip the SDK's own signing +await synapse.storage.addPieces({ dataSetId: 42n, pieces, extraData }) +``` + +## Step 4 — rotate or remove the authorizer + +Attach a different contract to rotate, or `address(0)` to detach and return the data set to the +default payer/session-key behavior: + +```ts +await payer.writeContract({ + address: WARM_STORAGE, + abi: setAuthorizerAbi, + functionName: 'setDataSetAuthorizer', + args: [42n, '0x0000000000000000000000000000000000000000'], +}) +``` + +Detaching is immediate and always available to the payer, so a malfunctioning authorizer can +never permanently lock a payer out of their own data set. + +## Session keys vs programmable ACLs + +Both approaches delegate signing away from the payer wallet; they solve different problems +and can be used together (a session key for routine UX, an authorizer for a specific policies +on specific data sets). + +| | [Session keys](/developer-guides/access-control/session-keys/) | Programmable ACLs | +| --- | --- | --- | +| **What it is** | An SDK-native ephemeral **secp256k1** key with on-chain permission grants | **Your own contract** deciding each write | +| **Where policy lives** | The shared `SessionKeyRegistry` (per-key, per-operation, time-boxed grants) | Arbitrary logic in your authorizer | +| **Curves / auth** | secp256k1 (EVM signatures) | Anything — P256 passkeys/WebAuthn, multisig, thresholds, oracles | +| **SDK support** | First-class (`@filoz/synapse-core/session-key`, `Synapse({ sessionKey })`) | `extraData` passthrough + `viem` (no dedicated helper yet) | +| **Scope** | Per session key, across all your data sets | Per data set | +| **Reach for it when** | You want silent signing / better dApp UX with the standard model | You need a curve or policy the built-in check can't express | + +Session keys remain the recommended default for ordinary "sign once, operate silently" UX. +Reach for a programmable ACL when you need something the standard model can't do — most commonly +**passkey/WebAuthn authorization** or **custom on-chain policy**. + +## Caveats + +- **Gas.** A P256/passkey authorizer verifies via the FEVM secp256r1 precompile, which is expensive + on Filecoin (~100M-120M gas for a full passkey `isAuthorized`). The storage provider that relays + your operation must supply enough gas; FWSS caps the authorizer sub-call at 150M gas. +- **`extraData` size.** Space for the auth envelope in `extraData` is limited to 1024 bytes. + Compact signatures (a raw P256 `(r, s)`) fit comfortably; a full WebAuthn passkey envelope is + larger (~600–750 bytes), so ensure that the auth data is as compact as possible. +- **Replay.** FWSS enforces replay protection for **add-pieces** (a per-payer nonce baked into the + digest). The schedule-removals and terminate digests are **not** nonce-bound, so if your policy + needs replay protection for those, your authorizer must provide it (e.g. consume its own nonce). +- **Direct termination is not gated.** An attached authorizer gates *signed (immediate)* termination + only. It does **not** restrict a payer or the storage provider from terminating a data set via the + plain, unsigned path. Do not rely on an authorizer to gate or prevent termination. + +## Next Steps + +- [Session Keys](/developer-guides/access-control/session-keys/) — the SDK-native delegation model. +- [Storage Operations](/developer-guides/storage/storage-operations/) — add-pieces, removals, and + termination that accept the `extraData` override. +- [Filecoin Warm Storage Service](/core-concepts/fwss-overview/) — how FWSS authorizes writes. diff --git a/docs/src/content/docs/developer-guides/session-keys.mdx b/docs/src/content/docs/developer-guides/access-control/session-keys.mdx similarity index 94% rename from docs/src/content/docs/developer-guides/session-keys.mdx rename to docs/src/content/docs/developer-guides/access-control/session-keys.mdx index 6db6f54d8..84d8c59d2 100644 --- a/docs/src/content/docs/developer-guides/session-keys.mdx +++ b/docs/src/content/docs/developer-guides/access-control/session-keys.mdx @@ -2,9 +2,23 @@ title: Session Keys description: Delegate signing permissions to ephemeral keys for improved UX and security. sidebar: - order: 6 + order: 1 --- +:::tip[Alternative: programmable ACLs] +Session keys are the recommended default for silent, popup-free signing with the standard secp256k1 +model. It is fast and cheap and works with raw Synapse SDK as well as FWSS. + +However if you need different signing algorithms or more detailed per-dataset delegation (eg +**P256 passkey** (Touch ID) a multisig, or per-operation rate-limits) then FWSS also offers +[Programmable ACLs](/developer-guides/access-control/programmable-acls/), which delegate the write-authorization +decision for a data set to a contract you supply. + +The two are complementary and can be used together on different datasets belonging to the same owner. See +[Which should I use?](/developer-guides/access-control/programmable-acls/#session-keys-vs-programmable-acls) +for more details. +::: + ## What are session keys? Session keys are ephemeral signing keys that can perform a limited set of operations on behalf of a root wallet. They are registered on-chain via the **SessionKeyRegistry** contract, which stores permission grants as time-limited authorizations. diff --git a/docs/src/content/docs/developer-guides/index.md b/docs/src/content/docs/developer-guides/index.md index 0ae0362d9..ac855c702 100644 --- a/docs/src/content/docs/developer-guides/index.md +++ b/docs/src/content/docs/developer-guides/index.md @@ -22,7 +22,8 @@ This page describes the available Synapse packages and how to choose the one tha - **Payments**: Deposits, withdrawals, operator approvals ([Payments Operations Guide →](/developer-guides/payments/payment-operations/)) - **Storage**: Upload and download files to storage providers ([Storage Operations Guide →](/developer-guides/storage/storage-operations/)) - **Provider Discovery**: Query registered storage providers and products -- **Session Keys**: Delegate signing authority for automated workflows +- **Session Keys**: Delegate signing authority for automated workflows ([Session Keys Guide →](/developer-guides/access-control/session-keys/)) +- **Programmable ACLs**: Delegate write authorization to your own contract — passkey/WebAuthn or custom policy ([Programmable ACLs Guide →](/developer-guides/access-control/programmable-acls/)) [**Synapse SDK Guide →**](/developer-guides/synapse/) diff --git a/docs/src/content/docs/getting-started/index.mdx b/docs/src/content/docs/getting-started/index.mdx index 791359ffc..c0f567ab4 100644 --- a/docs/src/content/docs/getting-started/index.mdx +++ b/docs/src/content/docs/getting-started/index.mdx @@ -216,7 +216,8 @@ You've just stored and retrieved data on Filecoin. See the [Developer Guides](/d - [Upload Pipeline](/developer-guides/storage/upload-pipeline/) - From simple one-liner to manual store, pull, and commit control - [Storage Operations](/developer-guides/storage/storage-operations/) - Data set management, retrieval, and lifecycle - [Payment Operations](/developer-guides/payments/payment-operations/) - Fund your account and manage storage payments -- [Session Keys](/developer-guides/session-keys/) - Delegate signing to ephemeral keys for better UX +- [Session Keys](/developer-guides/access-control/session-keys/) - Delegate signing to ephemeral keys for better UX +- [Programmable ACLs](/developer-guides/access-control/programmable-acls/) - Rich delegation of dataset control to a smart contract - [API Reference](/reference/filoz/synapse-sdk/toc/) - Complete SDK classes, methods, and types - [Example Application](https://github.com/FIL-Builders/foc-upload-dapp) - Full-stack upload dapp From 61d5f2131f670754078de3c31bb94735489cfa71 Mon Sep 17 00:00:00 2001 From: JAG-UK Date: Sun, 2 Aug 2026 16:29:49 +0100 Subject: [PATCH 2/4] fix: update better docs --- .../access-control/programmable-acls.mdx | 70 +++++++++---------- .../access-control/session-keys.mdx | 20 +++--- .../access-control/which-to-use.mdx | 55 +++++++++++++++ 3 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 docs/src/content/docs/developer-guides/access-control/which-to-use.mdx diff --git a/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx index 2a3598e1b..64869651a 100644 --- a/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx +++ b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx @@ -2,21 +2,13 @@ title: Programmable ACLs description: Delegate the entire write-authorization decision for a data set to your own on-chain contract (Data Set Authorizers). sidebar: - order: 2 + order: 3 --- :::tip[Alternative: session keys] -Programmable ACLS in FWSS provide rich, fine-grained per-dataset, per-operation control using a -smart contract that you supply. They enable you to use different signing algorithms and more detailed -per-dataset delegation (eg **P256 passkey** (Touch ID) a multisig, or per-operation rate-limits) than -the default mechanism. +FWSS offers 2 distinct methods of controlling write access to data sets. -However where such fine control is not necessary, [session keys](/developer-guides/access-control/session-keys/) -are the recommended default for silent, popup-free signing with the standard secp256k algorithm. They are fast and -cheap and work with raw Synapse SDK as well as FWSS. - -The two are complementary and can be used together on different datasets belonging to the same owner. See -[Which should I use?](#session-keys-vs-programmable-acls) for more details. +Ensure you pick the right one by consulting [which should I use?](/developer-guides/access-control/which-to-use/) first. ::: ## What are programmable ACLs? @@ -73,21 +65,6 @@ The `digest` is FWSS's EIP-712 digest for that exact operation and its parameter is whatever blob your authorizer expects (FWSS does not interpret it); `operationData` is the raw ABI-encoded operation payload, so a policy can gate on contents (it is empty for terminate). -:::caution[The authorizer is the *sole* gate for the operations it covers] -While attached, the authorizer **fully replaces** the session-key signature check for -add-pieces, schedule-removals, and *signed (immediate)* termination. FWSS does no signature recovery -of its own for those — your contract owns the whole decision, **including verifying whatever -`signature` it expects over `digest`**. Exercise extreme diligence in security review of any -authorizer contract you deploy. Ideally you should use an FWSS-provided contract and only write -your own if you need functionality that is not already covered. -::: - -:::caution[Gas limits] -In order to mitigate SP griefing the entire `isAuthorized` check is hard limited to 150M gas. -This is enough to implement something sophisticated like a touchID passkey verifier but you -still need to be careful an write optimized code when writing or deploying your own authorizer. -::: - ## Step 1 — write and deploy an authorizer Here is a minimal authorizer that accepts a **P256 (secp256r1) signature** over the operation @@ -119,13 +96,21 @@ contract SingleP256Authorizer { Deploy it with your usual tooling (Foundry, Hardhat, or `viem`'s `deployContract`), passing your P256 public-key coordinates to the constructor. +:::caution[Gas limits] +In order to mitigate SP griefing the entire `isAuthorized` check is hard limited to 150M gas. +This is enough to implement something sophisticated like a touchID passkey verifier but you +still need to be careful an write optimized code when writing or deploying your own authorizer. +::: + ## Step 2 — attach it to a data set -*** JAGTAG *** See if we can update the ABI to prevent this being necessary. +:::note[ABI preview] +Because the SDK's bundled FWSS ABI predates this method, the below example uses an inline ABI fragment +with `viem`. As soon as this feature reaches GA this will be cleaned up. +::: Attaching is a call to `setDataSetAuthorizer(dataSetId, authorizer)` on the FWSS contract. **Only the -data set's payer may call it.** Because the SDK's bundled FWSS ABI predates this method, use an -inline ABI fragment with `viem`: +data set's payer may call it.**: ```ts import { createWalletClient, http, getAddress, type Hex } from 'viem' @@ -160,18 +145,28 @@ or `address(0)` to detach (see Step 4). You can read the current authorizer back `getDataSetAuthorizer(uint256) → address` view (or `getDataSetAuthorizer` on the FWSS **State View** contract). -## Step 3 — authorize operations with `extraData` +:::caution[The authorizer is the *sole* gate for the operations it covers] +While attached, the authorizer **fully replaces** the payer and session-key signature check for +add-pieces, schedule-removals, and *signed (immediate)* termination. **There is no payer bypass** +so if the you still wish to call FWSS operations direct from the data set payer then they must be +recognized by the authorizer too. + +To protect the payer wallet from too much unnecessary exposure the preferred route would be to +create a dedicated delegate key and register it with the authorizer contract, but if for any reason +this fails the payer can always detach the authorizer with `setDataSetAuthorizer(dataSetId, address(0))` +and return to the default signature path. +::: -:::caution[Total delegation of all operations] -Once an authorizer is attached, FWSS delegates all access control decisions to it and the session -key registry is not checked. You must therefore include checks for all operations in the authorizer -(or make calls from the payer address, which will always succeed). +## Step 3 — authorize operations with `extraData` FWSS operations that can be gated by the authorizer are: - AddPieces - SchedulePieceRemovals - TerminateService +Because all access control decisions are passed to the registered authorizer, it must handle all of +these operations, else they will always revert. + Build the blob your `isAuthorized` implementation expects and pass it as the pre-built **`extraData`** on the operation: @@ -228,8 +223,13 @@ Session keys remain the recommended default for ordinary "sign once, operate sil Reach for a programmable ACL when you need something the standard model can't do — most commonly **passkey/WebAuthn authorization** or **custom on-chain policy**. -## Caveats +## Security Considerations & Caveats +- **Full responsibility.** When attached to a dataset the authorizer contract takes over **all* + responsibility for access controls, **including verifying whatever `signature` it expects over + `digest`**. Exercise extreme diligence in security review of any authorizer contract you deploy. + Ideally you should use an FWSS-provided contract and only write your own if you need functionality + that is not already covered. - **Gas.** A P256/passkey authorizer verifies via the FEVM secp256r1 precompile, which is expensive on Filecoin (~100M-120M gas for a full passkey `isAuthorized`). The storage provider that relays your operation must supply enough gas; FWSS caps the authorizer sub-call at 150M gas. diff --git a/docs/src/content/docs/developer-guides/access-control/session-keys.mdx b/docs/src/content/docs/developer-guides/access-control/session-keys.mdx index 84d8c59d2..e5e7829e5 100644 --- a/docs/src/content/docs/developer-guides/access-control/session-keys.mdx +++ b/docs/src/content/docs/developer-guides/access-control/session-keys.mdx @@ -2,21 +2,13 @@ title: Session Keys description: Delegate signing permissions to ephemeral keys for improved UX and security. sidebar: - order: 1 + order: 2 --- -:::tip[Alternative: programmable ACLs] -Session keys are the recommended default for silent, popup-free signing with the standard secp256k1 -model. It is fast and cheap and works with raw Synapse SDK as well as FWSS. +:::tip[Which access control method to use?] +FWSS offers 2 distinct methods of controlling write access to data sets. -However if you need different signing algorithms or more detailed per-dataset delegation (eg -**P256 passkey** (Touch ID) a multisig, or per-operation rate-limits) then FWSS also offers -[Programmable ACLs](/developer-guides/access-control/programmable-acls/), which delegate the write-authorization -decision for a data set to a contract you supply. - -The two are complementary and can be used together on different datasets belonging to the same owner. See -[Which should I use?](/developer-guides/access-control/programmable-acls/#session-keys-vs-programmable-acls) -for more details. +Ensure you pick the right one by consulting [which should I use?](/developer-guides/access-control/which-to-use/) first. ::: ## What are session keys? @@ -351,4 +343,8 @@ const expirations = await SessionKey.getExpirations(publicClient, { ## Next Steps +- [Programmable ACLs](/developer-guides/access-control/programmable-acls/) — fine-grained per-dataset operation access control on FWSS. +- [Storage Operations](/developer-guides/storage/storage-operations/) — add-pieces, removals, and + termination that accept the `extraData` override. +- [Filecoin Warm Storage Service](/core-concepts/fwss-overview/) — how FWSS authorizes writes. - [Session Keys API](/reference/filoz/synapse-core/session-key/toc/#functions): Reference documentation diff --git a/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx b/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx new file mode 100644 index 000000000..9d6070810 --- /dev/null +++ b/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx @@ -0,0 +1,55 @@ +--- +title: Which Access Control System To Use +description: Which access control system to use for your data sets +sidebar: + order: 1 +--- + +## Two types of access control delegation + +FWSS offers 2 distinct ways of controlling access to dataset write operations (addPieces, +schedulePieceDeletion, and terminateService). Which you should choose will depend on your use +case and may even vary between data sets. + +The two are complementary and can be used together on different datasets belonging to the same +owner. + +### Session Keys + +Session keys allow the payer to delegate its authority to update data sets to a secp256k1 +key, enabling operations without the need for constant wallet authorizations from the +payer wallet. This is a simple, coarse delegation that empowers the session key holder +to update any dataset belonging to the payer. + +### Programmable ACLs + +Programmable ACLS in FWSS provide rich, fine-grained per-dataset, per-operation control using a +smart contract that you supply. They enable you to use different signing algorithms and more detailed +per-dataset delegation than the default mechanism (eg P256 passkey (Touch ID), a multisig, or +per-operation rate-limits) . + +## Session keys vs programmable ACLs + +Both approaches delegate signing away from the payer wallet; they solve different problems +and can be used together (a session key for routine UX on basic data sets, an authorizer for +specific policies on specific data sets). + +| | [Session keys](/developer-guides/access-control/session-keys/) | [Programmable ACLs](/developer-guides/access-control/programmable-acls/) | +| --- | --- | --- | +| **What it is** | An SDK-native ephemeral **secp256k1** key with on-chain permission grants | **Your own contract** deciding each write | +| **Where policy lives** | The shared `SessionKeyRegistry` (per-key, per-operation, time-boxed grants) | Arbitrary logic and storage in your authorizer contract | +| **Curves / auth** | secp256k1 (EVM signatures) | Anything — P256 passkeys/WebAuthn, multisig, thresholds, oracles - so long as they fit within [the limits](/developer-guides/access-control/programmable-acls/#security-considerations) | +| **SDK support** | Applies to native Synapse and FWSS | FWSS only | +| **Scope** | All your data sets at once | Fine-grained per data set | +| **Reach for it when** | You want silent signing / better dApp UX with the standard model | You need an algorithm or policy the session key registry can't express | + +Session keys are the recommended default for ordinary "sign once, operate silently" UX. They are fast and +cheap and work with raw Synapse SDK as well as FWSS. + +Reach for a programmable ACL when you need something the standard model can't do — most commonly +things like **passkey/WebAuthn authorization** or **custom on-chain policy**. + +## Next Steps + +- [Session Keys](/developer-guides/access-control/session-keys/) — the SDK-native delegation model. +- [Programmable ACLs](/developer-guides/access-control/programmable-acls/) — fine-grained per-dataset operation access control on FWSS. From bae2892884beb7847c063fe752728035b986732a Mon Sep 17 00:00:00 2001 From: JAG-UK Date: Sun, 2 Aug 2026 22:54:05 +0100 Subject: [PATCH 3/4] fix: broken hash link --- .../docs/developer-guides/access-control/which-to-use.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx b/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx index 9d6070810..087b8e7c2 100644 --- a/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx +++ b/docs/src/content/docs/developer-guides/access-control/which-to-use.mdx @@ -38,7 +38,7 @@ specific policies on specific data sets). | --- | --- | --- | | **What it is** | An SDK-native ephemeral **secp256k1** key with on-chain permission grants | **Your own contract** deciding each write | | **Where policy lives** | The shared `SessionKeyRegistry` (per-key, per-operation, time-boxed grants) | Arbitrary logic and storage in your authorizer contract | -| **Curves / auth** | secp256k1 (EVM signatures) | Anything — P256 passkeys/WebAuthn, multisig, thresholds, oracles - so long as they fit within [the limits](/developer-guides/access-control/programmable-acls/#security-considerations) | +| **Curves / auth** | secp256k1 (EVM signatures) | Anything — P256 passkeys/WebAuthn, multisig, thresholds, oracles - so long as they fit within [the limits](/developer-guides/access-control/programmable-acls#security-considerations--caveats) | | **SDK support** | Applies to native Synapse and FWSS | FWSS only | | **Scope** | All your data sets at once | Fine-grained per data set | | **Reach for it when** | You want silent signing / better dApp UX with the standard model | You need an algorithm or policy the session key registry can't express | From fa97df7d06bdada0fb0c4b1ab6376d38628a3bc2 Mon Sep 17 00:00:00 2001 From: JAG-UK Date: Tue, 18 Aug 2026 10:29:01 +0100 Subject: [PATCH 4/4] Add whitelist wording --- .../developer-guides/access-control/programmable-acls.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx index 64869651a..4752e277e 100644 --- a/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx +++ b/docs/src/content/docs/developer-guides/access-control/programmable-acls.mdx @@ -233,6 +233,13 @@ Reach for a programmable ACL when you need something the standard model can't do - **Gas.** A P256/passkey authorizer verifies via the FEVM secp256r1 precompile, which is expensive on Filecoin (~100M-120M gas for a full passkey `isAuthorized`). The storage provider that relays your operation must supply enough gas; FWSS caps the authorizer sub-call at 150M gas. +- **Providers may not accept every authorizer.** Because the storage provider fronts the gas for + `isAuthorized`, a provider can restrict which authorizer *code* it is willing to relay for — + typically an allowlist of audited implementations, matched by code identity rather than address (so + you can run your own isolated instance of approved code, e.g. an EIP-1167 clone). A bespoke + authorizer may be rejected at add-pieces / removal / terminate time unless the provider recognizes + it. Prefer an FWSS-provided / audited authorizer (or a clone of one), or check with your provider + before relying on a custom one. - **`extraData` size.** Space for the auth envelope in `extraData` is limited to 1024 bytes. Compact signatures (a raw P256 `(r, s)`) fit comfortably; a full WebAuthn passkey envelope is larger (~600–750 bytes), so ensure that the auth data is as compact as possible.