Skip to content

the-pines/ocean

Repository files navigation

Ocean

Ocean is a collaborative, Notion-style workspace app powered by Arkiv.

Screenshot 2026-03-06 at 10 06 21 PM

Live app | Demo video

Features

Workspaces

  • 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

Pages & Editor

  • 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 become active automatically

AI Writing Assistant

  • Cmd+J or /ai slash command to open an AI prompt panel
  • Streaming responses powered by Claude (Anthropic API)
  • Generate or edit markdown content from natural language instructions

Search & Filtering

  • 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

Authentication & Authorization

  • 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, or auto (direct with relayer fallback)

UI & Navigation

  • 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

Architecture

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

Core product flows

  • 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)

Tech stack

  • 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 integration

  • 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

Setup

  1. Install dependencies:
pnpm install
  1. Copy env template:
cp .env.example .env.local
  1. Configure required variables:
  • NEXT_PUBLIC_PRIVY_APP_ID
  • NEXT_PUBLIC_OCEAN_WRITE_MODE=relayer|direct|auto
  • RELAYER_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_ID
  • NEXT_PUBLIC_PRIVY_SIGNER_ID
  • PRIVY_APP_ID (server-side override)
  • NEXT_PUBLIC_ARKIV_RPC_URL
  1. Run app:
pnpm dev -p 3001
  1. Quality checks:
pnpm typecheck
pnpm test
pnpm lint

Arkiv data model

All entities include base attributes:

  • app = "ocean"
  • schemaVersion = "1"

All payloads are JSON with contentType=application/json.

1) ocean.workspace

Purpose: workspace metadata and owner identity

Queryable attributes:

  • workspaceId
  • ownerAddress
  • name
  • nameLower
  • createdAtTs
  • updatedAtTs

2) ocean.workspaceMember

Purpose: append-only membership lifecycle events

Queryable attributes:

  • workspaceId
  • memberAddress
  • role (editor/removed)
  • invitedBy
  • eventAtTs

3) ocean.pageRevision

Purpose: append-only revision history for pages

Queryable attributes:

  • workspaceId
  • pageId
  • parentId
  • title
  • titleLower
  • authorAddress
  • updatedAtTs
  • isRoot
  • lifecycle (draft/active)

Lifecycle and integrity

Page lifecycle (draft -> active)

Lifecycle is derived from page content and indexed as a queryable Arkiv attribute.

  • draft: untitled + empty markdown
  • active: all other pages

This lifecycle is used for:

  • query-time filtering
  • search filter UI
  • workspace overview stats

Relationship integrity

  • 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 integrity

Membership events are validated with typed signatures and reduced to latest-per-address effective state.

Query model usage

Ocean uses Arkiv predicates and sorting patterns beyond plain ID lookup:

  • and(...) for core entity scoping
  • or(...) for role/address combinations
  • eq(...) for typed attribute filters
  • gte(...) 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

Write modes

  • relayer (default): server writes to Arkiv using relayer key
  • direct: user wallet writes directly
  • auto: direct first, relayer fallback

Relayer mode is the default abstraction path for product UX.

Error handling

  • 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 and filtering UX

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

Arkiv TTL strategy

Differentiated expirations by entity purpose:

  • Workspace: 5 years
  • Membership (editor): 3 years
  • Membership (removed): 180 days
  • Page revision: 2 years

Other things

  • ARKIV.md to see where exactly in the code we enforce the Arkiv flow
  • ISSUES.md for issues we can into when building with Arkiv

About

own your workspace

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors