Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ COPY packages/config/package.json packages/config/
COPY packages/core/package.json packages/core/
COPY packages/db/package.json packages/db/
COPY packages/enrichers/package.json packages/enrichers/
COPY packages/knowledge/package.json packages/knowledge/
COPY packages/notify/package.json packages/notify/
COPY packages/payments/package.json packages/payments/
COPY packages/queue/package.json packages/queue/
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ Free pages and feeds carry one [CrawlProof](https://crawlproof.com) ad and a tra

Training crawlers (GPTBot, ClaudeBot, CCBot, meta-externalagent…) get a 402 with an x402 offer and buy a pass at `/crawl`: $1 a day for everything, and the more a buyer has paid here the less a day costs (`CRAWL_LOYALTY`, default 20% off after $10, 40% after $50, 60% after $100; every sale is a row in `crawl_sales`). A pass may switch ads or tracking off for its own requests with `?disable=ads,tracking`. People, search engines and retrieval crawlers pass through untouched.

## Knowledge Influencers

Every collection here is also a **niche** someone can operate. A Knowledge
Influencer is a person who knows how an industry actually works and supervises
the agents building software, data and promotion for it — no code required. They
start at **20%** of what the niche makes and climb to **80%** as verified
contribution accumulates.

`/opportunities` lists the niches looking for one. `/<niche>` is the niche's
public page (plus `skill.md` and `manifest.json` for agents), `/@<handle>` is an
operator's profile, `/dashboard/niches` is their own view, and
`/admin/knowledge` is where claims and contributions are verified.

Score comes only from contributions somebody verified, and volume does not buy
it: repeated submissions of a type pay less each time, anything claiming a
customer or a payment needs an outside reference, and a duplicate books once.
The ladder lives in `contribution_tiers`, so a deployment can tune it. See
[docs/knowledge-influencers.md](docs/knowledge-influencers.md) and
[docs/revenue-share.md](docs/revenue-share.md).

## Stack

Bun, Hono (server-rendered JSX), Postgres via Bun's native driver, BullMQ on Redis, Biome. `bun apps/worker/src/enrich-cli.js [n]` runs enrichment from a terminal. Shared Profullstack modules: `@profullstack/x402-gateway`, `@profullstack/emailer`, `@profullstack/coinpay`, `@profullstack/referrals`, `@profullstack/api-key-manager`, `@profullstack/autoblog` (signed webhooks), `@profullstack/favicon-generator`.
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@nichedb/core": "workspace:*",
"@nichedb/db": "workspace:*",
"@nichedb/enrichers": "workspace:*",
"@nichedb/knowledge": "workspace:*",
"@nichedb/notify": "workspace:*",
"@nichedb/payments": "workspace:*",
"@nichedb/queue": "workspace:*",
Expand Down
29 changes: 29 additions & 0 deletions apps/web/public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -608,3 +608,32 @@ tr.dim td {
border: 0;
border-radius: 8px;
}

/* the knowledge influencer ladder, and the evidence an admin reads */
.tiers tr.here td {
font-weight: 600;
}
.dimensions {
list-style: none;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
gap: 0.25rem 1rem;
}
.dimensions .dim-name {
color: var(--muted);
text-transform: capitalize;
}
.cta-block {
border: 1px solid var(--line);
border-radius: 10px;
padding: 1rem 1.25rem;
}
/* Submitted and crawled text. It wraps and it never becomes markup. */
td.evidence {
font-family: var(--mono, ui-monospace, monospace);
font-size: 0.75rem;
color: var(--muted);
max-width: 22rem;
overflow-wrap: anywhere;
}
8 changes: 8 additions & 0 deletions apps/web/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { gateway, gatewayFor } from './lib/pricing.js';
import { Denied } from './lib/service.js';
import { registerApi } from './routes/api.js';
import { registerAuth } from './routes/auth.js';
import { registerKnowledge } from './routes/knowledge.js';
import { registerManage } from './routes/manage.js';
import { registerMcp } from './routes/mcp.js';
import { registerPages } from './routes/pages.js';
Expand Down Expand Up @@ -97,6 +98,13 @@ registerManage(app);
registerApi(app);
registerMcp(app);

/**
* Last, because a niche's page is served from the site root: every other
* route is registered before `/:slug` can be asked. A niche may not take a
* slug the site already uses, so the two can never compete for a name.
*/
registerKnowledge(app);

app.notFound(async (c) => {
if (wantsJson(c)) return c.json({ error: 'not found' }, 404);
return c.html(await render(<NotFound user={c.get('user')} />), 404);
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/lib/pricing.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ export function gatewayOptions(priceCents) {
// recruiting page would be an odd way to run a marketplace.
'/sell',
'/sell/',
// Same reasoning on the other side of the marketplace: the page that
// tells someone they can be paid for what they know is not the page to
// charge them to read.
'/opportunities',
'/opportunities/',
],
onSale: async (sale) => {
console.log('[x402] sold a pass', {
Expand Down
Loading
Loading