Ocean is a collaborative, Notion-style workspace app powered by Arkiv.
- Create, rename, and switch between workspaces
- Invite members by wallet address or email (with Resend-powered invite emails)
- Owner-only controls for renaming and removing members
- Workspace overview with page counts, active/draft stats, and last activity
- Hierarchical page tree with parent/child relationships and expand/collapse
- Markdown editor with rich automatic formatting (tables, task lists, code blocks with syntax highlighting, images, links)
- Slash command menu — insert images, links, dividers, tables, code blocks, child pages, add a calendar block, or invoke AI to write something for you!
- Calendar blocks (Notion-style month grid) powered by child pages rendered as calendar events
- Wiki links (
[[Page Title]]) that resolve to existing pages or create new ones - Autosave with checkpoint history stored on Arkiv, plus manual save via Cmd+S
- Page lifecycle modeling — pages start as
draft(untitled + empty) and becomeactiveautomatically
- Cmd+J or
/aislash command to open an AI prompt panel - Streaming responses powered by Claude (Anthropic API)
- Generate or edit markdown content from natural language instructions
- Cmd+K search modal with keyword matching across titles and content
- Combinable filters: match scope (title/content), lifecycle (active/draft), structure (root/child), updated window (24h/7d/30d)
- Sort by relevance, most recent, or title A–Z
- Query match highlighting in results
- Wallet-based auth via Privy (supports Google, email, and wallet login)
- EIP-712 typed-data signatures for all write intents
- Gasless UX via relayer mode (server submits transactions on behalf of the user)
- Three write modes:
relayer(default),direct, orauto(direct with relayer fallback)
- Collapsible sidebar with page tree, workspace switcher, settings, and logout
- Light/dark theme toggle (Cmd+Shift+L) with system preference fallback and no-flash loading
- Mobile-responsive drawer sidebar
- Keyboard shortcuts: Cmd+K (search), Cmd+J (AI), Cmd+S (save), Cmd+N (new page), Cmd+\ (toggle sidebar)
- Animated underwater-themed landing page
Browser (Next.js client)
-> Privy auth + wallet context
-> Signs typed-data intents (EIP-712)
-> Calls relay API routes
Relay API routes (Next.js server)
-> Validate payload schema (zod)
-> Verify EIP-712 signatures
-> Verify authorization against Arkiv state
-> Submit Arkiv writes with relayer wallet
Arkiv (Kaolin)
-> Stores append-only entities
-> Queried by attributes + orderBy
-> Client derives current workspace/page state from entity history
- Create workspace
- Rename workspace
- Invite/remove member (wallet or email invite flow)
- Create/edit pages with autosave checkpoints
- Search pages with combinable filters:
- match scope (title/content)
- lifecycle (
active/draft) - structure (root/child)
- updated time windows
- sort (relevance/recent/title)
- Next.js App Router + React + TypeScript (strict)
- Arkiv SDK (
@arkiv-network/sdk) on Kaolin - viem for EIP-712 signing/verification
- Privy (
@privy-io/react-auth) - TipTap markdown editor
- Resend for invite emails
- Vitest for logic tests
- Arkiv as a structured data layer (not a key-value storage)
- Multi-entity modeling (
workspace,workspaceMember,pageRevision) - Queryable attributes with ordering and predicate composition
- Wallet-based authorization with EIP-712 intent signatures
- Relayer write mode for gasless UX
- Entity lifecycle modeling (
draft/active) and lifecycle-aware filtering
- Install dependencies:
pnpm install- Copy env template:
cp .env.example .env.local- Configure required variables:
NEXT_PUBLIC_PRIVY_APP_IDNEXT_PUBLIC_OCEAN_WRITE_MODE=relayer|direct|autoRELAYER_PRIVATE_KEY(required for relayer mode)PRIVY_APP_SECRET(required for email invite lookup)RESEND_API_KEY(required for invite notification emails)OCEAN_INVITE_EMAIL_FROM(verified sender in Resend)NEXT_PUBLIC_APP_URL(base URL for invite links)
Optional but recommended:
NEXT_PUBLIC_PRIVY_POLICY_IDNEXT_PUBLIC_PRIVY_SIGNER_IDPRIVY_APP_ID(server-side override)NEXT_PUBLIC_ARKIV_RPC_URL
- Run app:
pnpm dev -p 3001- Quality checks:
pnpm typecheck
pnpm test
pnpm lintAll entities include base attributes:
app = "ocean"schemaVersion = "1"
All payloads are JSON with contentType=application/json.
Purpose: workspace metadata and owner identity
Queryable attributes:
workspaceIdownerAddressnamenameLowercreatedAtTsupdatedAtTs
Purpose: append-only membership lifecycle events
Queryable attributes:
workspaceIdmemberAddressrole(editor/removed)invitedByeventAtTs
Purpose: append-only revision history for pages
Queryable attributes:
workspaceIdpageIdparentIdtitletitleLowerauthorAddressupdatedAtTsisRootlifecycle(draft/active)
Lifecycle is derived from page content and indexed as a queryable Arkiv attribute.
draft: untitled + empty markdownactive: all other pages
This lifecycle is used for:
- query-time filtering
- search filter UI
- workspace overview stats
- Parent links are validated at write time
- Self-parent and missing-parent writes are rejected
- Cycle detection rejects invalid parent updates
- Read-time normalization also strips invalid parent relationships
Membership events are validated with typed signatures and reduced to latest-per-address effective state.
Ocean uses Arkiv predicates and sorting patterns beyond plain ID lookup:
and(...)for core entity scopingor(...)for role/address combinationseq(...)for typed attribute filtersgte(...)for time windows (updatedAtTs)orderBy(...)for deterministic ordering
Main usage areas:
- workspace latest materialization
- membership event streams
- revision streams
- lifecycle/structure/time-window capable revision queries
relayer(default): server writes to Arkiv using relayer keydirect: user wallet writes directlyauto: direct first, relayer fallback
Relayer mode is the default abstraction path for product UX.
- All relay endpoints validate schema and return structured errors
- Signature/authz failures return actionable messages
- Client normalizes direct/relay/network errors to user-readable text
- Sidebar workspace/page creation now surfaces inline errors instead of silent promise failures
Search supports combined filters and immediate result updates:
- keyword query
- title/content scope
- lifecycle scope
- root/child structure scope
- updated recency windows
- relevance/recent/title sorting
Differentiated expirations by entity purpose:
- Workspace: 5 years
- Membership (
editor): 3 years - Membership (
removed): 180 days - Page revision: 2 years