A decentralized knowledge base built on Arkiv.
Datalog lets you create documentation spaces, write pages in Markdown, and own your content through wallet-based ownership. All data lives on-chain as Arkiv entities -- no traditional database, no centralized storage.
Think GitBook or Notion, but every document is an on-chain entity with wallet-bound ownership and built-in expiration.
Built for the Arkiv Web3 Database Builders Challenge -- Knowledge Base vertical.
| Feature | Description |
|---|---|
| Knowledge Spaces | Group related documentation into named spaces with description and icon |
| Markdown Pages | Full GFM Markdown support with live preview, code blocks with copy button, and auto-generated table of contents |
| Page Tree | Nested pages via parentPageKey attribute, rendered as a collapsible GitBook-style sidebar |
| Version History | Every edit snapshots the previous content as a Revision entity on Arkiv |
| Bookmarks | Readers can save pages for quick reference |
| Expiration Management | All entities expire -- visible badges with one-click extend |
| Public Read Access | Anyone can browse spaces and read pages without a wallet |
| Wallet Ownership | Authors own their content via Ethereum wallet; only the owner can edit or archive |
| Status Lifecycle | Pages transition between draft, published, and archived states |
Datalog uses Arkiv as its sole data layer. There is no database, no file storage, no backend state outside of on-chain entities.
The application defines 4 entity types, each with distinct expiration durations reflecting their real-world lifecycle:
Space (365 days)
|
+-- Page (180 days)
| |
| +-- Page (nested, 180 days)
| |
| +-- Revision (30 days)
|
+-- Bookmark (90 days)
| Entity | Attributes | Payload | Expiration | Rationale |
|---|---|---|---|---|
| Space | type, name, slug, status |
JSON (name, description, icon) | 365 days | Spaces are long-lived containers; a year gives authors time to maintain them |
| Page | type, spaceKey, parentPageKey, title, slug, status, order, version |
Markdown content | 180 days | Documentation pages need periodic review; 6 months balances longevity with freshness |
| Revision | type, pageKey, version, summary, timestamp |
Markdown snapshot | 30 days | Edit history is useful short-term; old revisions can expire without data loss |
| Bookmark | type, pageKey, spaceKey |
JSON (note) | 90 days | Bookmarks are personal references; 3 months is enough to revisit or let go |
All queries use the SDK's predicate-based query builder with eq() from @arkiv-network/sdk/query:
| Operation | Query |
|---|---|
| List active spaces | eq("type", "space"), eq("status", "active") |
| Pages in a space | eq("type", "page"), eq("spaceKey", key), eq("status", "published") |
| Draft pages | eq("type", "page"), eq("spaceKey", key), eq("status", "draft") |
| Search by title | eq("type", "page"), eq("title", query), eq("status", "published") |
| Page revisions | eq("type", "revision"), eq("pageKey", key) |
| All bookmarks | eq("type", "bookmark") |
Every query chains .withAttributes(), .withPayload(), and .withMetadata() to fetch complete entity data.
- Writes use a server-side wallet client (private key in
.env). The wallet owner is the author of all entities. - Reads use a public client -- no wallet required. Anyone can browse spaces, read pages, and search.
- Entity ownership is enforced by Arkiv: only the wallet that created an entity can update or extend it.
- Status lifecycle transitions:
draft->published->archived(pages),active->archived(spaces) - Entity updates with revision tracking: editing a page creates a Revision entity with the previous content, then updates the page with new content and incremented version
- Expiration extension: owners can extend any entity's expiration via the
/api/extendendpoint - Bookmark as entity: bookmarks are themselves Arkiv entities with their own expiration, demonstrating that even user preferences live on-chain
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, Server Components) |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS v4 + @tailwindcss/typography |
| Data layer | Arkiv via @arkiv-network/sdk v0.6.0+ |
| Markdown | react-markdown + remark-gfm |
| Package manager | Bun |
| Testnet | Arkiv Kaolin |
- Bun v1.0+ (or Node.js 18+)
- An Ethereum wallet with test ETH from the Kaolin faucet
git clone https://github.com/YOUR_USERNAME/datalog.git
cd datalog
bun installcp .env.example .envEdit .env with your wallet credentials:
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
ADDRESS=0xYOUR_ADDRESS_HEREYou can generate a new wallet using any Ethereum wallet tool. Then request test ETH from the Kaolin faucet.
bun devOpen http://localhost:3000.
bun run build
bun startdatalog/
src/
app/ # Next.js App Router
page.tsx # Landing page (hero + featured spaces)
spaces/
page.tsx # Browse all spaces
[spaceKey]/
layout.tsx # Space layout with sidebar
page.tsx # Space overview
[pageKey]/
page.tsx # Page reader (Markdown + TOC)
author/
page.tsx # Author dashboard
spaces/
new/page.tsx # Create space form
[spaceKey]/
page.tsx # Manage space + pages
pages/
new/page.tsx # Markdown editor (create)
[pageKey]/edit/ # Markdown editor (edit)
search/page.tsx # Search results
api/ # REST API routes
spaces/ # POST create, GET list
pages/ # POST create, PUT update, DELETE archive
bookmarks/ # POST create, DELETE remove
search/ # GET search by title
extend/ # POST extend expiration
components/
ExpirationBadge.tsx # Color-coded expiration with extend button
LoadingSkeleton.tsx # Animated loading placeholders
PageContent.tsx # Markdown renderer with code copy
PageEditor.tsx # Write/preview editor with tabs
PageTree.tsx # Recursive nested page sidebar
SearchBar.tsx # Search input with navigation
SpaceCard.tsx # Space card + grid components
TableOfContents.tsx # Auto-generated from headings
Toast.tsx # Success/error notifications
lib/
arkiv/ # Arkiv SDK abstraction layer
client.ts # Public + wallet client setup
constants.ts # Expiration durations (4 values)
types.ts # TypeScript interfaces
spaces.ts # Space CRUD operations
pages.ts # Page CRUD + search + lifecycle
revisions.ts # Revision creation + queries
bookmarks.ts # Bookmark CRUD
extend.ts # Entity expiration extension
index.ts # Barrel exports
utils.ts # Address truncation, date formatting
challenge-context/ # Original challenge documentation
- Browse -- Visit the landing page or
/spacesto see all active knowledge spaces - Read -- Click into a space, navigate the page tree, read Markdown content
- Search -- Use the search bar to find pages by title
- Bookmark -- Save a page for later (creates a Bookmark entity)
- Create Space -- Go to Author > New Space, fill in name/description/icon
- Create Page -- Inside a space, create pages with the Markdown editor (write + preview)
- Nest Pages -- Set
parentPageKeyto create sub-pages (tree navigation) - Edit Page -- Edit content; previous version is saved as a Revision entity
- Manage Lifecycle -- Publish drafts, archive old content, extend expirations
- Archive -- Archive spaces or pages (status transition, still on-chain)
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/spaces |
List all active spaces |
POST |
/api/spaces |
Create a new space |
GET |
/api/spaces/:key |
Get a single space |
DELETE |
/api/spaces/:key |
Archive a space |
POST |
/api/pages |
Create a new page |
GET |
/api/pages/:key |
Get a single page |
PUT |
/api/pages/:key |
Update page content (creates revision) or change status |
DELETE |
/api/pages/:key |
Archive a page |
GET |
/api/search?q=term |
Search pages by title |
GET |
/api/bookmarks |
List all bookmarks |
POST |
/api/bookmarks |
Create a bookmark |
DELETE |
/api/bookmarks |
Remove a bookmark |
POST |
/api/extend |
Extend an entity's expiration |
Deployed on Vercel. All reads happen server-side via Arkiv's public client; writes go through API routes using the server wallet.