The problem that sparked this project
Here's something that bothered me about Web3 developer tools: they're often built by experienced blockchain developers for experienced blockchain developers.
In the case of IP registration via blockchain, if you're a solo creator who just wants to register your artwork, music, or code as intellectual property on-chain, you're looking at 15-30 minutes of fumbling through documentation, manually formatting JSON metadata, and praying your transaction doesn't fail after you've already spent gas.
I built Story CLI to fix that.
What I built
Story CLI is a command-line toolkit for registering IP assets on Story Protocol—a blockchain designed specifically for programmable intellectual property. Instead of writing code or crafting API calls, you get an interactive wizard:
story register ./my-artwork.jpg
That's it. The CLI walks you through license selection, handles IPFS uploads, executes the blockchain transaction, and gives you a shareable portfolio visualization of all your registered IP.
The goal was simple: take a 15-30 minute process and compress it to under 5 minutes.
1. The license wizard state machine
One of the trickiest parts was translating legal license configurations into something a human could answer in 30 seconds. Story Protocol uses PIL (Programmable IP License) with multiple parameters—commercial use permissions, derivative rights, royalty percentages.
I mapped this to a 3-question decision tree:
Commercial use? → Yes/No
Allow derivatives? → Yes/No
Revenue share? → 0-100% (only if commercial + derivatives)
These three questions deterministically map to one of 4 license configurations:
| Commercial | Derivatives | Result |
|---|---|---|
| No | No | Non-commercial Only |
| No | Yes | Non-commercial Derivatives |
| Yes | No | Commercial No-Derivatives |
| Yes | Yes | Commercial Remix (+ royalty %) |
The state machine approach meant I could validate answers in real-time and prevent invalid combinations before they ever hit the blockchain.
2. Fail-fast everything
Blockchain transactions cost gas. Failed transactions still cost gas. This created a design imperative: validate everything before touching the chain.
- Wallet address format? Checked before any network call
- IPFS hash format? Validated at input
- Sufficient balance? Queried before transaction submission
- Royalty percentage? Bounded 0-100 at prompt time
The philosophy: if something's going to fail, fail in the first 2 seconds, not after a 30-second transaction attempt.
3. Three-part error messages
Every error in Story CLI follows a structure: What went wrong → Why it matters → How to fix it.
✖ Pinata API key not found
IPFS uploads require Pinata authentication for metadata storage.
Run: story config set pinataApiKey YOUR_KEY
This sounds obvious, but most CLI tools just dump "Error: invalid credentials" and leave you to figure it out. I spent real time ensuring every failure state had actionable guidance.
4. Self-contained portfolio HTML
After registration, users needed to see their IP. I chose Mermaid.js for graph visualization because it lets me generate a single HTML file with everything embedded—CSS, JavaScript, and relationship diagrams.
No server required. Download the file, email it to someone, and open it. It works.
story portfolio
# → generates story-portfolio.html with interactive graphs
The tradeoff was less visual customization than D3.js would offer, but for an MVP, shipping > perfection.
Challenges & what I learned
Challenge 1: SDK documentation gaps
Story Protocol is relatively new. The SDK documentation had gaps—especially around error responses and edge cases. I ended up reading SDK source code directly and building a mock implementation (STORY_CLI_MOCK=true) so I could develop offline without burning testnet ETH.
Lesson: When integrating new SDKs, budget time for exploration. Mock modes aren't just for testing—they're essential for iteration speed.
Challenge 2: Terminal UX is harder than it looks
Making a CLI feel "good" requires attention to details you take for granted in web UIs:
- Spinners during async operations (Ora)
- Color-coded output for visual hierarchy (Chalk)
- Boxed success messages for celebration moments (Boxen)
- Clear prompt validation with inline feedback (Inquirer)
Each library handles a specific UX need. Combining them into a cohesive experience took more iteration than expected.
Lesson: CLI UX is a real discipline. Users notice when it's done well—they just don't notice consciously.
Challenge 3: Config file security
Storing API keys and wallet information in ~/.storyrc required thinking about permissions. The config file is created with chmod 600 (owner read/write only), and sensitive values can be overridden via environment variables for CI/CD pipelines.
export STORY_WALLET_PRIVATE_KEY=0x... # Override config file
story register ./asset.png
Lesson: Security isn't a feature—it's a constraint that shapes your entire design.
The Tech Stack
Architecture at a glance
User terminal
│
▼
Command router (Commander.js)
│
├──► Register command
│ ├── License wizard (Inquirer.js)
│ ├── Metadata prompts
│ ├── IPFS upload (Pinata SDK)
│ └── Blockchain transaction (Story SDK)
│
├──► Portfolio command
│ ├── Asset fetching (Story API)
│ ├── Graph building (Mermaid.js)
│ └── HTML rendering
│
├──► Config command
│ └── ~/.storyrc management
│
└──► Status command
└── Wallet & network info
Key patterns used:
- Command pattern — Each CLI command is an isolated handler
-
Facade pattern —
StoryClientwraps the complex SDK -
Singleton pattern —
ConfigManagerprevents redundant file reads - Fail-fast validation — Errors surface immediately, not after blockchain calls
Why this matters (Beyond the code)
IP registration on blockchain is one of those "obviously useful" ideas that's been stuck behind technical barriers. The people who would benefit most—independent artists, open-source developers, content creators—are often the least equipped to navigate Web3 tooling.
Story CLI is my attempt at a bridge. Not a dumbed-down version, but a well-designed version that respects users' time and treats errors as communication opportunities rather than dead ends.
The goal was never to abstract away the blockchain entirely—it was to remove the friction while preserving the power.
Try it yourself
npm install -g story-cli
story --help
Quick start
# Configure your wallet
story config set walletAddress 0xYourAddress
story config set network testnet
# Set up IPFS (Pinata)
story config set pinataApiKey YOUR_KEY
story config set pinataApiSecret YOUR_SECRET
# Register your first IP asset
story register ./my-artwork.jpg
# View your portfolio
story portfolio
What's next
- Batch registration for multiple assets
- License template sharing
- Integration with popular creative tools
- Mainnet deployment guides
Links:



Top comments (0)