← Social Software

Social SoftwareTechnical Reference

This page covers the technical detail behind the Social Software ecosystem as implemented in the current codebase: database schema, API surface, business logic modules, MCP server, federation protocol, and the reasoning behind key architectural decisions. For the product thinking and structural vision, start with the narrative.

Tech Stack

Application

TechnologyPurpose
Next.js 14Full-stack framework with API routes and server components
React 18Component model for all frontend pages
TypeScript 5.3Type safety across client and server, mirroring the SQL schema
Tailwind CSS 3.4Utility-first styling
Zod 3Runtime input validation on API routes

Infrastructure

TechnologyPurpose
Supabase (PostgreSQL + ltree + RLS)Database, auth, Row-Level Security, RPCs, real-time
Sandpack (CodeSandbox)Lightweight React-only sandbox for fork execution
Anthropic Claude APISchema-aware code generation and modification in the fork IDE
Stripe ConnectAutomated creator payouts (code-complete, untested against live keys)
MCP SDKModel Context Protocol server for IDE integration (Cursor, Claude Desktop, Windsurf)

Database Schema

The database is the source of truth. Every table, RLS policy, and RPC function was defined before any application code was written.

Users and identity

TablePurpose
profilesUsers: DID (decentralised identity), Stripe account, stake balance, voting power
sessionsTelemetry: user engagement, device fingerprint for Sybil resistance

An auto-profile trigger fires on user signup. Row-Level Security policies gate all access.

Four-tier layer tables

TablePurpose
connector_layersTier 2: thin adapters to external data sources, with source_type, connection_template, adapter code, SHA-256 code_hash, and ltree lineage
business_api_layersTier 3: orchestration and business logic with OpenAPI spec, connector_ids array, and ltree lineage
appsTier 4: forkable UI layers with code, lineage_path as ltree, SHA-256 code_hash, declared scopes, optional business_api_id. Core platform pages are marked with is_core_component and core_route.
app_manifestsCompositions: connector + business_api + ui_layer, with forked_from_id for manifest forking. The “app store” unit.
app_embedsTracks which apps embed which, so revenue and attribution flow across composition boundaries

Revenue and economics

TablePurpose
revenue_poolMoney entering the ecosystem: tips, API fees, bounties, sponsorships
distributionsRevenue splits to each ancestor in a UI layer lineage: generation, amount, settlement status
manifest_distributionsCross-tier revenue splits for composed apps: 40% UI, 35% Business API, 25% Connector
bountiesJob board: event-sourced with optimistic concurrency via version

Governance and moderation

TablePurpose
schema_proposalsProposed connector layer changes with proposer, SQL, and vote tallies
votesStake-weighted voting on proposals
flagsCommunity moderation: five unique flags auto-removes an app from discovery
eventsImmutable append-only audit trail: every mutation recorded as aggregate_type, aggregate_id, event_type, payload

Federation and self-hosting

TablePurpose
federation_nodesPeer registry: name, URL, public key, status, last seen
federation_importsLog of layers imported from remote nodes, with hash verification
protocol_tax1% protocol tax ledger for federated node revenue, settled via Stripe Connect
shell_configMaps routes to which fork of each core page is “live”

Infrastructure

TablePurpose
asset_refsBlob storage registry: content hash + storage URI
mcp_access_logAudit log of MCP tool invocations: client, tool called, layer type, actor

Database functions and views

Function / ViewPurpose
get_ancestors(app_id)Walks ltree upwards for UI layers, returns root → current (depth ASC)
get_descendants(app_id)Walks ltree downwards for UI layers, returns all forks
get_connector_ancestors(id)Walks ltree upwards for Tier 2 connector layers
get_business_api_ancestors(id)Walks ltree upwards for Tier 3 business API layers
increment_balance()Atomic balance update during revenue settlement
increment_tip_total()Atomic tip accumulator per app
increment_session_count()Atomic session counter for telemetry
refresh_api_usage_weights()Recalculates voting power: (last 30 days sessions) + (distributions received × 10)
settle_manifest_revenue()Cross-tier revenue settlement: 40% UI, 35% Business API, 25% Connector. Each tier's share cascades through its own lineage.
resolve_shell_route()Returns the app row for the currently configured fork of a given shell route
get_embed_count(app_id)Count how many apps embed a given app
creator_stats (view)Dashboard data aggregation per creator
layer_ecosystem_stats (view)Aggregate stats per tier: total layers, forks, sessions, max depth

RLS: embeds are publicly readable; only authenticated users can create embed records.

Business Logic

Seven library modules in src/lib/ implement the core business logic. All are pure functions or thin wrappers around Supabase RPCs, with no UI dependencies.

Lineage (lineage.ts)

Encodes the provenance DAG using PostgreSQL's ltree extension. Every app's full ancestry is stored in a single column.

FunctionPurpose
buildRootPath(appId)Creates root path: app_{uuidNoDashes}
buildForkPath(parentPath, forkId)Extends path: {parentPath}.fork_{uuidNoDashes}
getAncestors(appId)Calls get_ancestors RPC, returns root → current
getDescendants(appId)Calls get_descendants RPC, returns full subtree
buildTree(nodes)Transforms flat list into nested tree structure for rendering
generationLabel(n)Human-readable: maintainer, parent, grandparent, great-grandparent

Ancestor queries use the @> operator (ancestor contains descendant). Descendant queries use <@ (descendant is contained in ancestor). Both are single indexed queries, not recursive CTEs.

Revenue (revenue.ts)

Implements logarithmic decay distribution and settlement.

calculateSplit(amount, ancestors) — takes a total amount and the ancestor chain (root → current). Reverses the chain so index 0 = current maintainer, then applies 50% decay at each generation. The last ancestor receives whatever remains. Amounts below $0.000001 are dropped.

settleRevenue(appId, amount, source) — the full settlement pipeline:

  1. Record revenue entry in revenue_pool
  2. Walk ancestors using getAncestors() RPC
  3. Calculate split via calculateSplit()
  4. Insert into distributions table, calling increment_balance() RPC for each recipient
  5. Mark pool as settled, increment app tip total
  6. Append immutable event to audit trail

triggerStripePayouts(distributionIds) — Stripe Connect transfer for each unsettled distribution. Skips creators without a connected Stripe account and transfers below Stripe's $0.50 minimum. Code-complete but untested against live keys.

Linter (linter.ts)

The AI Verification Engine. Eight regex-based rules, each returning a severity (error or warning) and a human-readable message.

RuleSeverityWhat it catches
no-external-fetcherrorfetch("https://...") — only relative /api/* allowed
no-evalerroreval()
no-function-constructorerrornew Function()
no-infinite-looperrorwhile(true), for(;;)
no-cookie-accesserrordocument.cookie
no-location-redirecterrorwindow.location = ...
no-direct-storagewarninglocalStorage/sessionStorage — prefers api-client hooks
out-of-scope-apierrorDetects HTTP method in fetch options. A GET /api/bounties requires api/bounties:read. A POST /api/bounties requires api/bounties:write. A call to /api/apps/:id/embed requires api/apps:embed. Write scope implies read; embed scope implies read for apps.

Any error-severity issue blocks compilation. Warnings are informational. The linter runs on every keystroke in the fork IDE and on every MCP publish_layer call.

Layers (layers.ts)

CRUD operations for the four-tier architecture.

FunctionPurpose
listLayers(type, limit)Lists active layers by type, sorted by fork count
getLayer(type, id)Single layer with profile join
getLayerByHash(type, hash)Content-addressable lookup via SHA-256 hash
forkLayer(type, parentId, ...)Core fork operation: computes hash, extends ltree, increments parent fork count, records audit event
createManifest(ownerId, ...)Composes data + API + UI layer IDs into an app manifest
listManifests(limit)Published manifests with all tier layers expanded

Federation (federation.ts)

Peer-to-peer layer sharing between independent ecosystem nodes.

FunctionPurpose
getNodeInfo()This node's public identity: name, URL, layer counts, capabilities
getPublicLayers(type?, limit)Layers available for federation, across all three types
importRemoteLayer(url, id, type)Fetches from remote, inserts as local root (depth 0), records audit event
announceNode(name, url)Upserts peer node in the registry
getKnownNodes()Lists all registered federation peers with status and last-seen timestamps

Imported layers become local roots with their own lineage. They can be forked, built upon, and generate revenue within the importing node.

Seed (seed.ts)

Contains the genesis bounty board React code and the injected api-client.jsx that gates all network access through a controlled interface. The api-client also exports composition primitives: useEcosystemApp(appId) for fetching another app's code and metadata, and EcosystemEmbed for rendering another app inline in an isolated iframe. Used by the npm run seed script.

API Surface

18 API routes, each a thin orchestration layer: validate input, call business logic, return response.

Core app routes

RouteMethodsPurpose
/api/appsGET, POSTDiscovery feed (sorted by session_count) and genesis app publishing
/api/apps/[id]GETFull app detail with code, scopes, and lineage path
/api/apps/[id]/forkPOSTFork controller: lint → hash → extend ltree → increment parent fork count → record event
/api/apps/[id]/exportGETEject button: exports as a standalone Next.js project
/api/apps/[id]/embedGETServe app code + metadata for cross-app embedding. Used by the EcosystemEmbed component.

Bounty routes

RouteMethodsPurpose
/api/bountiesGETQuery by status (open, in_progress, completed)
/api/bountiesPOSTCreate bounty with event sourcing
/api/bountiesPATCHClaim or update: optimistic concurrency via version, claimer_id only set on claim transition

Revenue and lineage

RouteMethodsPurpose
/api/tipPOSTRevenue settlement: validates amount with Number.isFinite(), walks lineage, applies decay cascade
/api/lineage/[id]GETFull descendant tree with per-node revenue totals
/api/revenueGETEarnings query by app or user

AI, telemetry, governance

RouteMethodsPurpose
/api/ai-forkPOSTClaude API integration: validates auth token via supabaseAdmin().auth.getUser(), schema-aware code modification
/api/telemetryPOST, PATCHSession start and heartbeat
/api/governanceGET, POSTSchema proposals, stake-weighted voting, community flagging
/api/eventsGETImmutable audit trail filtered by aggregate type and ID

Layers, manifests, federation, MCP

RouteMethodsPurpose
/api/layersGETList by type (data, api, ui)
/api/manifestsGET, POSTApp manifest CRUD (compose layers into publishable apps)
/api/manifests/[id]GET, POSTManifest detail with full layer code; POST to fork a manifest with optional layer overrides
/api/mcpGET, POSTMCP server: GET returns human-readable info, POST handles JSON-RPC
/api/federationGET, POSTNode discovery, layer exchange, peer announcement

MCP Server

The MCP server exposes the ecosystem to AI coding tools via two transports:

Resources (context discovery)

URIReturns
ecosystem://registry/connector-layersAll active connector layers
ecosystem://registry/business-api-layersAll active business API layers
ecosystem://registry/ui-layersAll active UI layers
ecosystem://registry/manifestsPublished app manifests
ecosystem://layer/{type}/{id}/codeCode + metadata for a specific layer

Tools (9 total)

ToolPurpose
list_ecosystem_layersList active layers by type, with optional limit
get_layerGet one layer with full code and metadata
fork_layerCreate fork: compute hash, extend ltree, record event
publish_layerPublish new layer (alias for fork with description)
test_compositionSpin up temporary sandbox to test UI + API compatibility
create_app_manifestCompose layers into a publishable app
list_app_manifestsPublished manifests with layer details
get_app_codeGet full source code of a UI layer app, with embedding instructions
fork_manifestFork a composed app as a unit, optionally overriding individual layers

Both transports call the same business logic modules. There is no separate MCP backend.

Frontend

14 pages covering the full user journey from discovery through forking, composition, earning, and governance.

PagePurpose
/Discovery feed: apps sorted by session_count (real usage, not vanity metrics)
/loginSupabase auth with ?next= redirect support
/apps/[id]App viewer: Sandpack live preview + tip button + lineage tree inline + scopes + export
/apps/[id]/forkAI fork IDE: Sandpack editor + Claude chat + lint validation + publish
/apps/[id]/eventsImmutable event log for a specific app
/apps/newGenesis app creator (publish root of a new DAG)
/bountiesBounty board: list (filterable by status), create, claim
/lineage/[id]Lineage dashboard: SVG fork tree + revenue per node + cascade preview
/dashboardCreator earnings, published apps, voting power, pending payouts
/governanceSchema proposals, stake-weighted voting, flagging
/compositorApp store: layer matrix with published manifests, Run and Fork actions per manifest
/compositor/[id]Manifest runner: live sandbox preview + layer stack + revenue split + fork lineage
/compositor/newAssemble app from layer dropdowns (data + API + UI)
/metaPlatform metadata: registered core pages, shell config, system health

Architecture Decisions

Why ltree instead of Neo4j

The provenance DAG is the most important data structure in the system. The obvious choice would be a graph database. PostgreSQL's ltree was chosen instead for three reasons:

The trade-off: ltree represents tree paths, not arbitrary graphs. Multi-parent merges (app C forks both A and B) are not supported at the lineage level. Multi-parent composition happens at the manifest level instead.

Why Sandpack instead of WebContainers

WebContainers are the upgrade path for full-stack forks once the sandbox model is proven.

Why a custom linter instead of ESLint

The trade-off: regex-based linting is evadable. The mitigation is defence in depth — the linter catches accidental violations, while the Sandpack CSP enforces the network boundary structurally. A production system would add AST-level analysis.

Why event sourcing

Every state mutation appends an immutable row to the events table. No updates, no deletes.

Why content-addressable hashing

Every piece of code is SHA-256 hashed on publish. This enables:

Known Limitations

AreaStatusNotes
TypeScript typesManualSupabase types not generated via supabase gen types. Types are manually defined in supabase.ts.
Revenue atomicitySequentialsettleRevenue() does sequential inserts. A mid-loop failure would leave partial distributions.
Stripe payoutsUntestedtriggerStripePayouts() compiles but has never run against real keys.
Sybil resistanceMinimalfingerprint field exists but behavioural entropy scoring is not implemented.
Governance executionRecords onlyProposals and votes persist but do not trigger automated schema changes.
MCP transportSimplifiedHTTP endpoint creates a new server per request. Production needs persistent SSE/WebSocket.
AST blast-radiusNot builtWould require AST parsing of all active forks to simulate schema changes.
Blob storageSchema onlyasset_refs table exists. No IPFS/Arweave integration.