Skip to content
moshcoderPublic

About

pit.world MCP server: private web search (no API key) and Moshpit name lookups for AI agents. Also a web search engine, API, CLI and TUI.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

pit.world

Private search, and the front door to the Moshpit network.

An MCP server for private web search. pit mcp is a stdio Model Context Protocol server that gives any agent (Claude, Cursor, any MCP client) web search with no API key and no logging, plus Moshpit name lookups. It is in the official MCP Registry as world.pit/pit.

pit.world is a search engine that keeps nothing about you: no query logs, no cookies, no profile, no trackers, no JavaScript required. It is also where the Moshpit network lives in public: type a Moshpit name like chovy.hacker and it resolves through the registry, next to your web results.

# the web
https://pit.world/search?q=anything

# the CLI and TUI
bun add -g pitworld
pit search "private search engines"
pit resolve chovy.hacker
pit                      # interactive

# your agent
claude mcp add pit -- pit mcp

MCP server

claude mcp add pit -- bunx pitworld mcp
{ "mcpServers": { "pit": { "command": "bunx", "args": ["pitworld", "mcp"] } } }

Tools:

  • search: private web search (Google and Bing indexes, merged), returns titles, URLs and snippets.
  • moshpit_resolve: resolve a Moshpit name such as chovy.hacker to where it points.
  • moshpit_network: live numbers for the Moshpit network.
  • listing: a business listing by domain (who operates it, category, service area, services, price, hours, feed, ring membership).
  • pros: search the pro directory by category, place and words.
  • ring: the pit.world web ring, in order.
  • edit_listing: edit a listing you own, with its edit key.

No key is needed. PIT_URL points it at another pit.world instance (default https://pit.world).

What it does

  • Web search, privately. Your browser talks only to pit.world. pit.world asks the Google index (through Serper) and the Bing index (through DataForSEO) at the same time, merges the two lists, and tags each result with the engine that found it. The providers get the words, never your IP, browser, cookies or referrer. Linkup, Exa and Perplexity are fallbacks if both fail.
  • Moshpit names. A query that looks like label.ending is checked against the Moshpit registry, and a registered name gets a card with where it points.
  • Bangs. !gh hqtui, rust lifetimes !so, !pit chovy.hacker: straight to that site's search, without a referrer. The full list is at pit.world/about#bangs.
  • Default search engine. OpenSearch description at /opensearch.xml, suggestions at /suggest, and https://pit.world/search?q=%s for any browser.

Business listings, the pro directory and the web ring

  • Listings (/b/<domain>). Every site has one. Its owner claims it with a DNS TXT record or a homepage meta tag and gets an edit key (pitk_…, shown once, stored as a hash). The owner writes who operates it (an agent or person handle like riotcoder@profullstack.com, or an https link to an OpenAgent record or profile), the company and its site, a category, the service area (free text plus city, region, country), services, price range, hours, phone and links.
  • Listed by pit.world (seeded). pit.world can list a business itself from public information before its owner claims it: the business's own website, licensing and permit records where available, and public company directories. License facts are checked at the issuer (e.g. CSLB for California contractors); NicheDB's public OutreachGraph mirror is matched by exact website domain and adds attributed industry and observed website topics. Never an email address, and never quote routing: the page says it is unclaimed and written by pit.world, is indexable, and carries a "Is this your business? Claim it" button. Claiming hands it to the owner (pit.world never writes to it again) and the owner can take it out of the directory from the edit page. Startup adds submitted public listings and refreshes directory matches daily. To refresh from listings.db on the server: bun src/admin.ts enrich-nichedb [--force]; to seed a public entry: bun src/admin.ts seed FILE.json (an array of {domain, name, description, phone, category, area, city, region, country, services, links, license, licenseCheckedAt, featured}), bun src/admin.ts unseed DOMAIN, bun src/admin.ts views.
  • Imported from NicheDB (source nichedb). Companies in nichedb.dev/c/directory, the public half of OutreachGraph (a company by its domain, its industry and stack; never an email, phone or person), become unclaimed listings: imported in id order after a saved cursor, ten pages of 200 every 5 minutes until caught up, then hourly (PIT_DIRECTORY_IMPORT=off stops it). Only companies whose domain resolves to a public address are listed; adult and gambling ones never are. They follow every claimed and seeded listing in /pros (filtered and paged in SQL), carry "Unclaimed listing, data from nichedb.dev" with the record's link, the claim button and vote buttons. SEO guard: an imported listing is indexable and in sitemap.xml only with a real description (read once from its homepage the first time the page is opened) and a category; otherwise its page is noindex. Claiming one hands it to the owner like any other.
  • OpenProfile (/b/<domain>/openprofile.md). Every listing in the directory is also an OpenProfile.md with Kind: organization: web, category, location, area, phone, license, services, links, and whether the owner has claimed it. Found by <link rel="openprofile"> on the listing page and a Link header; GET /api/pros carries each one's openprofile URL. OutreachGraph reads them as a lead source.
  • Views. /b/<domain> counts page views by people (crawlers, link previews and scripts are not counted) for listings in the directory; the count is views in GET /api/listing.
  • Pro directory (/pros, /pros/<category>). Claimed listings and ones pit.world listed, browsable by a fixed list of categories (home services, legal, accounting, software and dev agencies, health, and so on: GET /api/pros/categories) and searchable by words and place. Paid featured placements first, clearly labeled and active only during their paid period, then editorial picks, verified owners by latest edit and unclaimed ones. No reviews in v1: there is no identity to anchor a review to that a business could not fake, so there are none rather than ones that can be bought.
  • Quote requests. A listing with a confirmed private address shows a "Request a quote" form. pit.world mails the owner from hello@pit.world with the sender as Reply-To; the owner's address is never shown, sent, or returned by the API. The address only works after its owner clicks the link mailed to it, so a listing cannot aim pit.world's mail at someone else. One listing per request; three requests an hour per visitor (HMAC'd IP, memory only), five an hour and twenty a day per listing, four hundred a day site-wide; a honeypot field; messages with more than three links are refused. pit.world keeps no copy: only the time, to count.
  • The pit.world ring (/ring). *.pit.world sites only: the core sites (pit.world, seo.rank, family.ancestry, ...) and then every claimed listing that is a pit.world subdomain or a Moshpit name (relayed at <name>.pit.world), in the order they joined: for those, claiming is joining, and owners can leave from the edit page. A business on its own ICANN domain can claim its listing and be in /pros, but is never a ring member (ringEligible in src/pros.ts). Hops: /ring/next|prev|random?from=<site> (also ?host=, ?url=, ?via=, or the Referer), 302 with no-store; /api/ring for members that draw the bar themselves; /ring/opml for every member's feed. The verified and edit pages show the links to paste.
  • Mirrored to rssamplifier.com. pit.world reads each claimed site's homepage for <link rel="alternate"> RSS/Atom on claim and daily, and keeps the ring pit-world on rssamplifier.com/ring/pit-world in step: submit new feeds, add members, remove ones that left. The sync is a diff (idempotent), runs a few seconds after a claim or edit and once a day, and needs RSSAMPLIFIER_API_KEY (an rssamplifier API key of the account that owns the ring; off without it). Moshpit-only names are not mirrored, since rssamplifier cannot reach them. New claims are also a feed: /b/feed.xml.

Public industrial directory listings

Powell Industries is listed as an unclaimed electrical-infrastructure company in src/directory-seeds.ts, using the existing server-side ensurePublicDirectorySeeds import. View it at /b/powellind.com or browse /pros/industrial. Business owners can claim their entry using the standard verification process. The legacy /pro route already redirects to /pros. This directory listing does not imply endorsement, partnership, or any acquisition by xAI.

Business metadata and company enrichment

Every company listing has a canonical website and a categorized directory page. /pros/industrial is the industrial electrical-power category, and /b/powellind.com is Powell Industries' profile. Both the directory cards and detail pages show the category prominently, along with website and publicly listed phone, when known. A JSON-LD Organization block on the detail page, the JSON API (/api/listing / /api/pros) and /b/<domain>/openprofile.md expose the same public facts to search engines and agents.

Two independent, daily source-driven enrichments run on the existing listings:

  • NicheDB / OutreachGraph: src/nichedb.ts matches the exact website hostname from NicheDB's public OutreachGraph directory mirror and adds attributed business/industry/technology topics. Only company records are accepted; person or site rows are not used as proof of a business association. The OutreachGraph public export intentionally does not expose emails, phone numbers, locations, or CRM leads. No private API token or workspace campaign data is used.
  • Official company website: src/official-company-sites.ts uses the existing SSRF-checked HTTPS verifier for unclaimed, seeded companies only, bounded to three concurrent requests, with a 24-hour refresh. src/company-metadata.ts extracts an explicitly linked same-domain contact page, team/leadership page, company social links, role-based public company inbox (info@, sales@, etc.), and a corporate phone only from matching on-domain Organization JSON-LD. It never guesses a person's address or imports CRM contact data. Each fact retains the company's site as its source. An owner claim hides these automatic facts rather than overriding owner-entered data. A missing or unavailable site remains un-enriched; no fake fields are filled.

People enrichment: PIT links to an official team/leadership page when one is explicitly published by the company. Do not import named prospects from OutreachGraph into PIT's public directory unless their company affiliation has explicit, authoritative public evidence, and only use their public, voluntarily published professional profiles. Keep private campaigns, scores, guessed emails, personal phone numbers, and unpublished OpenProfile.md files out of public profiles. This phase does not replicate OutreachGraph's people database.

Deployment note: changes to the code do not update a running process by themselves. After deployment, server startup migrates the listings SQLite database and refresh jobs fill metadata; verify GET /api/pros/categories, GET /api/listing?domain=powellind.com, and /pros/industrial to confirm live data and category counts.

Promotion campaigns: promote first, then show the results

A promotion campaign lists and promotes the businesses an OutreachGraph campaign targets, measures what that did for each one over its own 30-day window, and only then writes to them, with the numbers. It is how the Los Gatos builders campaign runs (pit.world/roundup/los-gatos-builders).

  • Import. scripts/outreachgraph-campaign-export.sh cmp_... writes a complete, read-only snapshot of the OutreachGraph campaign (OutreachGraph has no API that lists every member with its addresses). pit promo import ID snapshot.json gives every source row exactly one disposition (matched, merged into the same business, unresolved, excluded) and the totals must add up. Leads at one domain are one business with all their lead ids. Re-importing the same snapshot changes nothing; only a complete snapshot can mark a business as having left (its listing stays, its priority goes). Businesses without social profiles are kept.
  • The roster first. pit promo roster ID: lead ids, business, website, listing, verification, planned action. A member is publishable when its listing is in the directory with a name, category, place, description and a checked source (a license checked at the issuer, or an owner claim).
  • Outreach hold. A member only launches when every way the upstream could send to it is held: each lead suppressed in OutreachGraph (the los-gatos-promotion-hold suppress list) or only in campaigns that wait for approval. Otherwise it is blocked with the reason; an operator can attest a hold (pit promo set ID DOMAIN --attest TEXT).
  • Priority. Verified members of a running campaign come first in /pros, in SQL before pagination, after filters, so pages, the API and the mobile view agree. Turning priority off or stopping the campaign leaves the listing alone. Campaign membership is never shown publicly.
  • Roundup and spotlights. pit promo preview ID shows every planned post with its text and time and a version; pit promo launch ID --version V publishes /roundup/<slug> and creates the calendar: a roundup announcement now, then one spotlight per business across the configured accounts, perAccountPerDay each, from postHour local time. pit promo worker ID (run where myna is logged in) posts due ones with myna post <account> --front, reconciles every post against myna's history and queue by its tracking id (?via=plc_...) before posting so a crash never posts twice, and verifies each through the network's public API (Bluesky getPostThread, Mastodon statuses). Scheduled is not published, published is not verified; only verified posts count. LinkedIn has no public read, so its posts never count. Accounts on networks where myna buys a CrawlProof ad per post are refused: promotion is organic, and a paid budget defaults to zero.
  • Measurement. Listing views, website clicks (/b/<domain>/go, a redirect to the listing's own site only), phone taps (a beacon from /listing.js, deduplicated by its id), quote requests, and roundup views (campaign-level, never credited to each business). Crawlers, link previews, prefetches, PIT_INTERNAL_IPS and marked tests (?pt=test) are stored as excluded. No cookie, no IP, no user agent: a visitor is a hash under a salt deleted when its UTC day ends, so "visitor-days" exist and unique visitors over a window do not (reported as unavailable). Only businesses in a campaign are counted.
  • Moving off an account. When a network bans or locks an account, pit promo reassign ID --from ACCOUNT --to ACCOUNT [--dry-run] (run where myna is) moves every unposted placement (ready, scheduled, failed) to another account: same tracking id and planned time, text rewritten for the new network, back to ready so the worker re-queues it at the new account's pace. It cancels the old myna queue items, never moves a post myna already published (or one that is uncertain or mid-post), notes each move in the audit log and drops the old account from the campaign config.
  • Windows. Each business's window starts when its listing is live, its tracking passed a marked test (pit promo test-tracking ID) and a placement naming it was verified, and lasts periodDays (30) elapsed days. The hourly pass marks ended windows due and makes one review job per window (a restart never makes a second), which writes a versioned report and a draft. Late additions get their own window; gaps are recorded (pit promo gap), never hidden by moving dates.
  • Reports. Private and frozen per version (pit promo report ID --md); shareable at /r/<token> (HTML, .md, .json) without the operator's costs. Unavailable is not zero. Late data makes a new version and voids unsent drafts that quoted the old one. pit promo csv ID compares the campaign.
  • Drafts and sending. Each report gets one draft from its own numbers: an introduction unless a contact is on record (then a follow-up), an honest note when nothing was measured, blocked when there is no address. Sending is off until sendAuthorized; deliver re-checks the window, the report version and upstream suppression at send time (unknown means no) and keeps one delivery per recipient, message version and window.
  • Surfaces. API /api/v1/promotion/* (Bearer PIT_ADMIN_TOKEN), dashboard /admin/promo (a signed-in pit.world admin), pit promo ..., pit promo tui ID, and promo_* MCP tools when PIT_ADMIN_TOKEN is set.

One account for every pit.world site: social. and bbs.

Every Moshpit name gets two more addresses, with nothing to set up:

  • social.<name>.pit.world: the name's social feed. Posts from the AT Protocol network tagged with the name's niche (family.ancestry follows #genealogy, #familyhistory, #ancestry, ...), read from every relay we can reach at once (Bluesky's and the independent ones: atproto.africa, relay.fire.hose.cam, relay.upcloud.world, relay.waow.tech), plus posts by members. Each is also an AT Protocol feed generator (did:web:social.<name>.pit.world). It is the pit-social service; pit.world forwards the host to it.
  • bbs.<name>.pit.world: the name's bulletin board, a tsbb board made on its first visit (deploy/pit-bbs-provision), its own database, forums seeded, the pit.world admins as its administrators.

Both sign people in with their pit.world account: sign in once at pit.world/signin with CoinPay (OAuth 2.1, code + PKCE) and every board and feed signs you in through pit.world (/bridge/authorize, /bridge/token, @profullstack/bridges), silently when you are already signed in. Nobody makes an account per site. /account shows the sites you use, your CLI tokens, sign out and delete.

  • A site's client id is its host; its secret is base64url(HMAC-SHA256(PIT_BRIDGE_KEY, host)), so a new name works the moment it exists and no site is registered by hand. PIT_BRIDGE_CLIENTS adds exact extra hosts (our own apps on the relay).
  • Accounts link by CoinPay sub (the person's CoinPay DID) only, never by email (CoinPay reports every email unverified).
  • CLI: pit login (OAuth 2.1, public client, loopback redirect, PKCE S256, RFC 8252), pit whoami, pit logout, pit social <name>, pit bbs <name>. API: GET /api/v1/me with Authorization: Bearer pw_….
  • Certificates: one Let's Encrypt certificate per name covers <name>, social.<name> and bbs.<name> (deploy/pit-proxy-cert).
  • Settings: PIT_COINPAY_CLIENT_ID, PIT_COINPAY_CLIENT_SECRET (CoinPay client with callback https://pit.world/api/v1/coinpay/oauth/callback), PIT_BRIDGE_KEY, PIT_ADMIN_SUBS, PIT_SOCIAL_URL (default http://127.0.0.1:8794, off), PIT_BBS (off), PIT_BBS_MAX (default 100).

Credit codes and invites

  • Credit codes (@profullstack/redeem-credits) grant server_months: months of the $5-a-month Moshpit server sold on /start. Redeem on /account ("Have a code?"), POST /api/v1/redeem or pit redeem CODE. A month is spent on /account through the same order a paid month makes (names, username, SSH key to whoever sets up servers, PIT_BUILD_NOTIFY), marked as paid with a credit; if the order cannot be sent the month comes back. Names under our endings are already free, so codes never grant names. Mint on the box: bun $(readlink -f node_modules/.bin/redeem-credits) mint --db ~/.local/share/pit-world/accounts.db --unit server_months --amount 1 --label "who it is for".
  • Invites (@profullstack/affiliates): 3 per account, single use, 30 days, made on /account/invites, POST /api/v1/invites or pit invite. /i/<code> sends a person through sign-in, and only a brand-new account spends it. An accepted invite gives nothing beyond the way in.
  • Affiliate links: pit.world/join?ref=CODE (and /?ref=CODE) attributes a new account to the member, first touch, once. Promoter codes stay with the promoter program.
  • No cookie: the invite or ref rides the sign-in next URL. Tables credit_* and aff_* live in accounts.db.

Privacy, by construction

  • nginx writes no access log for the site (ACCESS_LOG=off in bin/install.conf), and the app logs nothing per request.
  • No cookies to search. The server strips Set-Cookie from every response except the sign-in and account routes, which set one only after you press Sign in.
  • Rate limiting keys on an HMAC of the IP under a secret regenerated every hour, in memory only.
  • Anonymous votes and form limits use a salted, hashed device fingerprint (@profullstack/browser-fingerprint), used only to stop one device voting many times or flooding a form. The fingerprint itself is never stored, only HMAC(secret, stable components). A result vote keeps a keyed hash of its search, never the words. See Votes.
  • Identical searches are cached in memory for ten minutes to avoid paying twice; the cache holds words and results, nothing about who asked, and never touches disk.
  • Strict CSP (default-src 'none', same-origin scripts and styles only), Referrer-Policy: no-referrer, results link straight to the site.

The page says the same in plain words: pit.world/privacy.

API

GET /api/search?q=…&n=10 { query, provider, cached, results: [{ url, title, description, host }], moshpit }
GET /api/resolve?name=chovy.hacker the registry's answer for a Moshpit name
GET /api/network endings claimed, registry events, recent claims, endings for sale
GET /api/bangs every bang
GET /api/listing?domain=… a listing (never the owner's contact address)
POST /api/listing/claim · POST /api/listing/verify claim a listing; the edit key comes back once
POST /api/listing edit, with Authorization: Bearer pitk_…; contactEmail sets the private quote address ("" clears)
POST /api/listing/quote { domain, name, email, phone?, message }: one quote request to one listing
GET /api/pros?c=legal&near=oakland&q=wills&n=20&page=1 the pro directory
GET /api/pros?source=pit claimed and pit.world-written listings only (source=nichedb: imported only)
GET /api/pros/categories categories (with counts) and price bands
GET /b/<domain>/openprofile.md a directory listing as an OpenProfile.md (Kind organization)
GET /api/ring the web ring, in order
GET /api/votes?kind=result&target=URL&q=… or ?item=listing:acme.com&item=… ▲ ▼ tallies { up, down, score } (read-only)
GET /api/votes/top?kind=listing&order=best|worst&n=20 best- or worst-voted items of a kind
GET /api/fp/challenge · POST /api/vote the browser's vote path: a challenge token, then { kind, target, value: 1|-1|0, toggle?, q?, fp }. Needs the page's fingerprint, so it is not an API for scripts

No key. Rate limited per visitor (429 with Retry-After). Full docs: pit.world/api.

Training corpus: /api/corpus

pit.world's own crawl index (pages pitbot read) as full text, paged, for training models. Served by pitbot's loopback index (services/crawler, GET /api/v1/corpus), metered and sold by pit.world (src/corpus.ts, src/corpus-routes.ts).

GET /api/corpus?q=saas,business,crypto&site=&kind=&lang=&since=&limit=1..100&cursor=
-> { total_estimate, next_cursor, resume_cursor, items: [{ url, title, site, kind, language, fetched_at, text }], access }
GET /api/corpus/stats      pages, excluded (and by which rule), sites, languages, kinds, last crawl (free)
GET|POST /api/corpus/credits?pages=10000   x402 credit pack -> key (x-corpus-key)
  • q: words that must all appear; commas are alternatives (saas,business,crypto = any). Each token is quoted before it reaches FTS5 MATCH, so punctuation is never a syntax error. Empty q pages the whole open corpus.
  • Keyset paging on the index id (never OFFSET): ids are stable across re-crawls, new pages get higher ids, so next_cursor never shifts and resume_cursor picks up pages added later. A page can hold fewer than limit items (rare matches are scanned in time-boxed slices); follow the cursor until it is null.
  • Price (PIT_CORPUS_USD_PER_1000, default 1): $1 per 1,000 pages returned. Free preview 20 pages a day per address with text cut to 500 characters. Over that, a 402 with the price, an x402 offer (body + PAYMENT-REQUIRED) and the other ways to pay: x402 on the request itself (limit x per-page price; unreturned pages become credit on a key), an x402 credit pack, or a CoinPay invoice from /account (balance used with a pit login token). PIT_CORPUS_INTERNAL_KEY as a bearer: free and unmetered (Profullstack's harvesters; also in vault global--shared as PIT_CORPUS_KEY).
  • Rate limit (@profullstack/throttle): 60 requests a minute per address, 1,200 for everyone.
  • Opt-outs (never in the corpus, still in search): noai in X-Robots-Tag or meta robots; TDMRep (tdm-reservation: 1 header or meta, /.well-known/tdmrep.json); robots.txt keeping an AI-training crawler (GPTBot, Google-Extended, CCBot, ClaudeBot, anthropic-ai, cohere-ai, Bytespider, PerplexityBot, Applebot-Extended and more, AI_TRAINING_AGENTS in services/crawler/src/robots.ts) off the path. Unknown is out: pages indexed before 0.28.0 are judged by a backfill on their site's robots.txt and tdmrep.json (and fully when re-crawled), and a robots.txt that could not be fetched keeps a site's pages out.
  • CLI pit corpus "saas,crypto" --limit 100 [--cursor N] (PIT_CORPUS_KEY), MCP tool corpus.

Votes

Every listing has a ▲ and a ▼: web results and every tab's results, the featured box, the sponsored slots (voted from pit.world's side of the sandboxed ad frame), PairUX/nixamp promos, RSS Amplifier blogs, the listing card, /b/<domain>, /pros and the home page's businesses. No account.

  • One vote per device and item (per search, for results): (visitorKey, item, qkey) is the key. Voting the other way replaces it; the same arrow again (toggle) takes it back.
  • Who is visitorKey from @profullstack/browser-fingerprint: /fp.js (served by pit.world, script-src 'self') collects canvas, WebGL, audio, fonts, screen, timezone, languages and client hints; the server checks a 10-minute challenge token bound to the visitor's /24 or /64, refuses bot-scored clients (navigator.webdriver, headless UA, software GL plus other tells), and keeps only HMAC(secret, stable components). The secret is PIT_FP_SECRET, or one generated once into $PIT_DATA_DIR/fingerprint.secret (0600).
  • Throttled hard: 12 votes a minute, 60 an hour, 200 a day per device, and per network (/24 or /64) 40 a minute, 300 an hour, 1,500 a day. Counters are in memory.
  • Without JavaScript the ▲ ▼ is a plain form to POST /vote, which explains that voting needs the fingerprint and links back.
  • Ranking (src/votes.ts): score = (up + 5) / (up + down + 10) − 0.5; a result moves 6 × score(this search) + 2 × score(its site, one vote per device per site) places, capped at 3, then results are re-ordered by position − moved. The featured network site leading a search keeps its place. /pros listings move by 6 × score, capped at 3, only among unpaid, non-campaign listings. Sponsored and featured slots are never re-ordered: their votes are a quality signal.
  • Storage: table votes in the listings SQLite database, created additively at startup. Back up the live database with SQLite's backup API before rollout.

Other anonymous writes (/subscribe, /api/subscribe, /b/<domain>/claim, /api/listing/claim, /b/<domain>/quote, /api/listing/quote, /submit, /api/submit, /build/invoice, and the anonymous allowance of POST /api/summarize) carry the same fingerprint when JavaScript is on (app.js adds an fp field; APIs may send x-fingerprint) and are limited per device with a looser per-network ceiling (src/visitor.ts). Without one they fall back to the old per-address limit.

Surfaces

The house rule: everything ships a TUI, a CLI, an MCP and an API.

Web + API bun src/server.ts (PORT, HOST; RESEND_API_KEY for the newsletter and quote requests; RSSAMPLIFIER_API_KEY (+ optional RSSAMPLIFIER_URL, RSSAMPLIFIER_RING) for the ring mirror; provider keys SERPER_API_KEY, DATAFORSEO_LOGIN + DATAFORSEO_PASSWORD (Bing), LINKUP_API_KEY, EXA_API_KEY, PERPLEXITY_API_KEY)
CLI pit search, pit resolve, pit network, pit open, pit listing, pit claim, pit verify, pit edit DOMAIN --key … --operator … --category …, pit pros [WORDS] --category … --near …, pit categories, pit quote DOMAIN --name … --email … --message …, pit votes KIND TARGET [--q WORDS], pit votes top KIND, pit ring
TUI pit
MCP pit mcp: tools search, moshpit_resolve, moshpit_network, listing, pros, ring, edit_listing, vote_tallies, vote_top. In the official MCP Registry as world.pit/pit (server.json)
PWA installable from any browser (/manifest.webmanifest, share target into /search); the worker in public/sw.js caches the static shell and an offline page, never a search

Art Auctions House

auctions.pit.world is the canonical Art Auctions House site. art.auctions.pit.world redirects there. Artists can submit a title, name, description and photo; submissions appear publicly in the gallery. Photos up to 8 MB are stored in the local auctions.db database, or the path in PIT_AUCTIONS_DB.

The gallery and photo submissions are live; bidding and payment are not active. Those terms need to be decided before transactions are added.

Newsletter (one list for every Profullstack property)

GET /subscribe shows the form; POST /subscribe (form: email, optional site) and POST /api/subscribe (JSON, CORS *) start a double opt-in. The @profullstack/footer subscribe form posts here with site=<its hostname>, so every signup keeps the property it came from; pit.world, *.pit.world and Moshpit names get pit.world copy, every other site gets Profullstack copy and a link back. An address already on the list still gets an email (its click records the new property).

Subscribers by property: /admin/subscribers (signed-in admin, PIT_ADMIN_SUBS) or with Authorization: Bearer $PIT_ADMIN_TOKEN: /api/admin/subscribers.json[?site=host&list], /api/admin/subscribers.csv[?site=host] (columns email,confirmed_at,source,sources). On the box: bun src/admin.ts subscribers [SITE].

The CLI, TUI and MCP talk to https://pit.world (or PIT_URL), so they need no keys.

Run your own

git clone https://github.com/moshcoder/pit.world && cd pit.world
bun install
SERPER_API_KEY=… bun src/server.ts        # http://127.0.0.1:8787

Production runs on bare metal with bin/install.sh (OpenInstall): nginx + Let's Encrypt + a systemd unit running bun.

The Moshpit network

Moshpit is a namespace beside ICANN's. Anyone can claim an ending (.hacker, .eggs, .pit), put names under it and point them anywhere; the registry is open and append-only, and it signs certificates, so the names are real HTTPS sites to anyone on the network. Get on it with moshcode (curl -fsSL https://moshcoding.com/install.sh | sh, then moshcode dns enable) or by setting your browser's secure DNS to https://dns.moshcode.sh/dns-query. Every method (moshcode, TronBrowser, browser DoH, an Apple profile, Windows, Android) and how to trust the Moshpit Root CA is at pit.world/connect; pit.world serves the root at /moshpit-root-ca.crt and the Apple profile at /moshpit.mobileconfig. Claim an ending at pit.moshcode.sh.

Daily Stitcher

daily.stitcher.pit.world is an exact pit.world host routed by the pit.world server. Its storefront uses the task-supplied quilt display photograph, cropped to remove the mail interface. The shop remains a coming-soon page until the owner supplies catalog and order details; no inventory, prices, checkout, contact information, or video channel are assumed.

Owner publishing uses the existing CoinPay OAuth 2.1 identity through pit.world. pit login authenticates the CLI with PKCE. bun bin/stitcher propose --title "…" --body "…" creates a private journal proposal; only the configured STITCHER_OWNER_SUB can publish or reject it with bun bin/stitcher approve <id> / bun bin/stitcher reject <id>. The bridge and CLI identify a person by CoinPay DID, never email. Sign-in does not authorize a payment. No one can publish while STITCHER_OWNER_SUB is unset.

The Moshpit registry name daily.stitcher is not registered. This exact web host can work because it is under pit.world's DNS, but claiming the separate .stitcher Moshpit ending is a paid action and is intentionally not part of deployment.

License

MIT

Featured businesses ($19/month)

The homepage links to the searchable /pros business directory. Category, service-word and location filters apply to paid placements too. Active paid placements have sponsoredUntil in the API and a “Featured · Paid placement” label on the homepage, directory and listing. A verified badge still means domain control, never payment or a quality endorsement. Free listings and existing editorial promotions stay available.

Owners manage a plan at /b/<domain>/featured, using the existing listing edit key (POST only, never in a URL). They explicitly accept monthly invoices and provide a private billing email. PIT_PAYAGENT_KEY and optional PIT_PAYAGENT_URL enable checkout using Profullstack's existing CoinPay merchant through pay.agent. The price is fixed server-side at $19. Each verified paid invoice buys one calendar month; the provider id, description and amount must match the stored invoice before activation. Merely returning from checkout cannot grant placement.

The production server checks pending invoices every minute. Three days before expiry it sends one renewal invoice for owners who left renewal on. This is monthly invoicing, not wallet auto-debit: customers pay each CoinPay invoice. Do not set CoinPay's recurring schedule too; local scheduling prevents double invoices and keeps the paid-period ledger bound to each verified invoice. “Stop future invoices” stops renewal creation and preserves paid time; any invoice already issued still requires the customer to choose to pay it. Expiry removes paid priority automatically at read time, even if billing is unavailable.

Tables featured_memberships and featured_invoices, plus listings.featured_paid_until, are created additively in the existing listings SQLite database at startup. Back up the live database with SQLite's backup API before rollout. No deployment seed or fixture is written to production. A provider timeout leaves the request blocked from replacement; the next tick reconciles against the authenticated merchant invoice list using the exact unique description and $19 amount. If no match can be confirmed, inspect that record and the merchant's invoices before resolving it; never blindly retry creation or grant time.

Billing email, invoice ids, renewal preference and payment dates remain private. Public API responses expose only the paid-through timestamp, alongside existing business information. Tests use fake invoices; publishing does not buy a placement or send an invoice.

About

pit.world MCP server: private web search (no API key) and Moshpit name lookups for AI agents. Also a web search engine, API, CLI and TUI.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages