PinataFS is a permissioned filesystem pattern for EVM chains.
This repository is organized as a spec-first repo:
- The root README defines the PinataFS model, assumptions, and scope.
- Solidity contracts in
smart_contract/are the reference on-chain implementation. - TypeScript tooling under
TypeScript/contains an SDK and demo front-end for quick testing.
PinataFS defines a standard shape for path-addressed content pointers on-chain, where write authority comes from NFT ownership.
Design goals:
- Deterministic path validation rules.
- Strict prefix-based write authorization.
- Simple last-write-wins semantics.
- Minimal on-chain state (latest value only) while preserving history via chain logs.
Two-contract model:
PermissionNFT(smart_contract/contracts/PermissionNFT.sol)PinataFS(smart_contract/contracts/PinataFS.sol)
Important:
PermissionNFTis a reference/example ERC-721 for workshops and quick starts.PinataFSis not coupled toPermissionNFT; it works with any ERC-721 contract that exposesownerOf(uint256).
Write permission is keyed by (nftContract, tokenId):
- Admin assigns writable prefixes to that key.
- Any current owner of that NFT may write to matching paths.
- NFT transfers move write capability automatically.
Contract-enforced rules:
- Must start with
/ - No duplicate slashes
- Cannot end with
/ - Segment charset:
A-Z,a-z,0-9,-,_ .allowed only in the final file segment (max one dot)- Prefixes cannot contain
.
Examples:
- Prefix:
/shared/data - File path:
/shared/data/sub/file1.json
Strict subtree matching:
/shared/dataallows/shared/data/sub/file1/shared/datadoes not allow/shared/database/file1
writeFileis upsert-only: latest CID overwrites prior CID for that path.getFilereturns latest CID or reverts if missing.fileExistsreports current presence.- CID formatting is intentionally not hard-enforced on-chain.
PinataFS.owner() can:
- Replace the full prefix set for
(nftContract, tokenId)(replaceTokenPrefixes) - Revoke/unrevoke writes for
(nftContract, tokenId)(setTokenWriteRevoked) - Transfer ownership, including to
address(0)to permanently disable admin writes
- Reads are public on public chains.
- Authorization depends on
ownerOf(tokenId)at write time. - If admin is burned (
transferOwnership(address(0))), permission config becomes immutable except by NFT transfers. - Historical changes are reconstructed from blocks/events; contract stores only latest CID per path hash.
smart_contractSolidity contracts and Foundry testsTypeScript/SDKTypeScript SDK (pinatafs-sdk)TypeScript/demoReact demo app
Prerequisites:
- Node.js
>=18(20+recommended) - pnpm
>=9 - Foundry (
forge)
Install:
pnpm installBuild and test:
pnpm build:contracts
pnpm test:contracts
pnpm --filter ./TypeScript/SDK build
pnpm --filter ./TypeScript/demo buildSet env vars:
export RPC_URL="https://sepolia.base.org"
export PRIVATE_KEY="0x<your_private_key>"
export CHAIN_ID="84532"
export CHAIN_NAME="Base Sepolia"
export CHAIN_CURRENCY_SYMBOL="ETH"Optional NFT metadata:
export PERMISSION_NFT_NAME="PinataFS Access"
export PERMISSION_NFT_SYMBOL="PFSA"Deploy both contracts:
pnpm deploy:stackIf you already have an external NFT collection, you can skip deploying PermissionNFT and deploy only the filesystem:
pnpm deploy:filesystemCreate env file:
cp TypeScript/demo/.env.example TypeScript/demo/.envRun:
pnpm devUse Coinbase Developer Platform faucet tooling: