@profullstack/builtindie 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/OpenStack.md +74 -0
- package/README.md +63 -2
- package/bin/builtindie +3 -0
- package/package.json +35 -4
- package/public/app.js +35 -0
- package/public/favicon.svg +1 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/icons/openicon.json +574 -0
- package/public/icons/svg/apple.svg +1 -0
- package/public/icons/svg/arrow-up.svg +1 -0
- package/public/icons/svg/bluesky.svg +1 -0
- package/public/icons/svg/check.svg +1 -0
- package/public/icons/svg/chrome.svg +1 -0
- package/public/icons/svg/code.svg +1 -0
- package/public/icons/svg/discord.svg +1 -0
- package/public/icons/svg/dollar.svg +1 -0
- package/public/icons/svg/external-link.svg +1 -0
- package/public/icons/svg/github.svg +1 -0
- package/public/icons/svg/globe.svg +1 -0
- package/public/icons/svg/instagram.svg +1 -0
- package/public/icons/svg/layers.svg +1 -0
- package/public/icons/svg/link.svg +1 -0
- package/public/icons/svg/linkedin.svg +1 -0
- package/public/icons/svg/mail.svg +1 -0
- package/public/icons/svg/product-hunt.svg +1 -0
- package/public/icons/svg/reddit.svg +1 -0
- package/public/icons/svg/rocket.svg +1 -0
- package/public/icons/svg/search.svg +1 -0
- package/public/icons/svg/share.svg +1 -0
- package/public/icons/svg/star.svg +1 -0
- package/public/icons/svg/tag.svg +1 -0
- package/public/icons/svg/telegram.svg +1 -0
- package/public/icons/svg/terminal.svg +1 -0
- package/public/icons/svg/tiktok.svg +1 -0
- package/public/icons/svg/trophy.svg +1 -0
- package/public/icons/svg/user.svg +1 -0
- package/public/icons/svg/x.svg +1 -0
- package/public/icons/svg/youtube.svg +1 -0
- package/public/install.sh +44 -0
- package/public/manifest.webmanifest +20 -0
- package/public/moshpit-root-ca.crt +11 -0
- package/public/style.css +162 -0
- package/public/sw.js +30 -0
- package/src/auth.ts +96 -0
- package/src/cli.ts +203 -0
- package/src/client.ts +115 -0
- package/src/db.ts +412 -0
- package/src/fetch.ts +708 -0
- package/src/html.ts +525 -0
- package/src/icons.ts +56 -0
- package/src/mcp.ts +79 -0
- package/src/media.ts +160 -0
- package/src/ring.ts +86 -0
- package/src/server.ts +655 -0
- package/src/service.ts +369 -0
- package/src/tui.ts +127 -0
- package/src/version.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Profullstack, Inc. (moshcoder)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/OpenStack.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# built.indie
|
|
2
|
+
|
|
3
|
+
- **Kind**: product
|
|
4
|
+
- **Web**: https://built.indie.pit.world
|
|
5
|
+
- **Repo**: https://github.com/moshcoder/built.indie
|
|
6
|
+
- **Operator**: https://built.indie.pit.world/.well-known/openprofile.md
|
|
7
|
+
- **License**: MIT
|
|
8
|
+
- **Updated**: 2026-10-19
|
|
9
|
+
|
|
10
|
+
Launch your indie product with one URL: we read the homepage and the LogicSRC files it serves, keep the pictures, and put it on a daily upvote board.
|
|
11
|
+
|
|
12
|
+
## Languages
|
|
13
|
+
|
|
14
|
+
- TypeScript 5: everything, ES modules
|
|
15
|
+
|
|
16
|
+
## Runtimes
|
|
17
|
+
|
|
18
|
+
- Bun 1.4: the server, the CLI, the TUI and the MCP server
|
|
19
|
+
- Browser: the PWA (service worker, manifest)
|
|
20
|
+
|
|
21
|
+
## Interfaces
|
|
22
|
+
|
|
23
|
+
### Web
|
|
24
|
+
|
|
25
|
+
- Hono 4: server-rendered pages, no client framework
|
|
26
|
+
- public/app.js: votes, copy buttons, progress, service worker
|
|
27
|
+
|
|
28
|
+
### API
|
|
29
|
+
|
|
30
|
+
- Hono 4: JSON under /api/v1, OpenSite index calls under /api/v1/sites
|
|
31
|
+
|
|
32
|
+
### CLI
|
|
33
|
+
|
|
34
|
+
- @profullstack/builtindie: `builtindie`
|
|
35
|
+
|
|
36
|
+
### TUI
|
|
37
|
+
|
|
38
|
+
- @profullstack/hqtui: the board in a terminal
|
|
39
|
+
|
|
40
|
+
### MCP
|
|
41
|
+
|
|
42
|
+
- @modelcontextprotocol/sdk 1: stdio, zod 3 schemas
|
|
43
|
+
|
|
44
|
+
## Data
|
|
45
|
+
|
|
46
|
+
- SQLite (bun:sqlite): one file, WAL
|
|
47
|
+
- Files: pictures beside the database, checked by their first bytes
|
|
48
|
+
|
|
49
|
+
## Services
|
|
50
|
+
|
|
51
|
+
- CoinPay: crypto payments for Featured
|
|
52
|
+
- Resend: sign-in links
|
|
53
|
+
- crawlproof: analytics and ads
|
|
54
|
+
- Headless Chrome: homepage screenshots, run as nobody
|
|
55
|
+
- pit.world relay: the address everyone without Moshpit DNS uses
|
|
56
|
+
|
|
57
|
+
## Specs
|
|
58
|
+
|
|
59
|
+
- OpenSite 0.1: every product is a record; we are a reader and an index
|
|
60
|
+
- OpenProfile 0.4: products are organizations, makers are people
|
|
61
|
+
- OpenStack.md 0.1: read from products, served about ourselves
|
|
62
|
+
- OpenSaaS 0.1: plans and the cancel page, read from products
|
|
63
|
+
- OpenIcon 0.1: the reference set, a vendored subset
|
|
64
|
+
|
|
65
|
+
## Hosting
|
|
66
|
+
|
|
67
|
+
- dev2 (bare metal): systemd unit built-indie, nginx, OpenInstall bin/install.sh
|
|
68
|
+
- Moshpit name built.indie, certificate from the Moshpit registry
|
|
69
|
+
|
|
70
|
+
## Never
|
|
71
|
+
|
|
72
|
+
- No Stripe: payments are CoinPay
|
|
73
|
+
- No client-side framework, no bundler
|
|
74
|
+
- No secrets in .env files: app.env is written from the vault
|
package/README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# built.indie
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Launch your indie product with one URL. **https://built.indie.pit.world** (on Moshpit DNS: https://built.indie)
|
|
4
|
+
|
|
5
|
+
Paste a URL and built.indie reads the homepage the way a careful person would: the name, tagline and description, the logo, the card image, screenshots from the page plus one we take ourselves with headless Chrome, YouTube/Vimeo/Loom/mp4 videos, social accounts, `llms.txt`, the web manifest and JSON-LD. It also reads the [LogicSRC](https://logicsrc.com/specs) files a site may serve about itself:
|
|
6
|
+
|
|
7
|
+
| file | spec | we show |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `/.well-known/opensite.json` | [OpenSite](https://logicsrc.com/docs/opensite) | what the site says it is; wins over what we scrape; `index.allow: false` is honoured |
|
|
10
|
+
| `/.well-known/openprofile.md` | [OpenProfile](https://logicsrc.com/docs/openprofile) | the company (`Kind: organization`) and its accounts |
|
|
11
|
+
| `/.well-known/openstack.md` | [OpenStack.md](https://logicsrc.com/docs/openstack) | "Built with" |
|
|
12
|
+
| `/.well-known/opensaas.json` | [OpenSaaS](https://logicsrc.com/docs/opensaas) | plans, trial, and the cancel page |
|
|
13
|
+
| `/.well-known/openicon.json` | [OpenIcon](https://logicsrc.com/docs/openicon) | that it publishes an icon set |
|
|
14
|
+
|
|
15
|
+
Then it goes on today's board. People upvote (one per visitor). The board resets at midnight UTC; yesterday's winners get "#1 Product of the day" on their page and badge.
|
|
16
|
+
|
|
17
|
+
**Free to launch, no account.** Anyone can launch any site. To edit a listing, sign in (an emailed link) and **claim** it by adding a DNS TXT record, `_built-indie.<domain>` → `built-indie-verify=<token>`. A signed-in submitter can edit until the owner claims it.
|
|
18
|
+
|
|
19
|
+
Viral bits: a live badge with the upvote count (`/badge/<slug>.svg`) that turns a link back into a followed link and a ✓ once we see it on the homepage, share buttons, an OG card per product, RSS, and an optional crypto-paid **Featured** slot (CoinPay).
|
|
20
|
+
|
|
21
|
+
Every product is published back as an OpenSite record (`/p/<slug>/opensite.json`) and an OpenProfile (`/p/<slug>/openprofile.md`); makers are OpenProfiles too (`/makers/<id>/openprofile.md`). Icons are the [OpenIcon](https://github.com/profullstack/openicon) reference set.
|
|
22
|
+
|
|
23
|
+
## Surfaces
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
curl -fsSL https://built.indie.pit.world/install.sh | sh
|
|
27
|
+
|
|
28
|
+
builtindie # TUI: today's board, ←/→ boards, v upvote, l launch
|
|
29
|
+
builtindie launch yourproduct.com
|
|
30
|
+
builtindie top week
|
|
31
|
+
builtindie show linear
|
|
32
|
+
builtindie vote linear
|
|
33
|
+
builtindie preview any.site --json
|
|
34
|
+
BUILTINDIE_KEY=bi_… builtindie claim linear # prints the TXT record, then checks it
|
|
35
|
+
BUILTINDIE_KEY=bi_… builtindie edit linear --tagline "…"
|
|
36
|
+
|
|
37
|
+
claude mcp add builtindie -- bunx @profullstack/builtindie mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The web app is a PWA (manifest + service worker). The JSON API is under `/api/v1` and documented at `/help`; the OpenSite index calls are `GET /api/v1/sites?url=` and `POST /api/v1/sites`.
|
|
41
|
+
|
|
42
|
+
## Run your own
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
bun install
|
|
46
|
+
BUILTINDIE_KEY=$(openssl rand -hex 32) bun src/server.ts # http://127.0.0.1:8798
|
|
47
|
+
bun test
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| env | |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `BUILTINDIE_KEY` | required: keys vote hashes and claim tokens |
|
|
53
|
+
| `BUILTINDIE_ORIGIN` | public address (default https://built.indie.pit.world) |
|
|
54
|
+
| `BUILTINDIE_DATA` | where the database and pictures live (default ~/.local/share/built-indie) |
|
|
55
|
+
| `BUILTINDIE_ADMIN_KEY` | bearer that may take a listing down |
|
|
56
|
+
| `RESEND_API_KEY`, `MAIL_FROM` | sign-in mail; without them the link is logged |
|
|
57
|
+
| `COINPAY_API_KEY`, `COINPAY_BUSINESS_ID`, `COINPAY_WEBHOOK_SECRET` | Featured payments |
|
|
58
|
+
| `FEATURE_USD`, `FEATURE_DAYS` | price and length of Featured (19, 7) |
|
|
59
|
+
| `BUILTINDIE_CHROME` | Chrome path, or `off`; `BUILTINDIE_SCREENSHOTS=off` skips screenshots |
|
|
60
|
+
| `CRAWLPROOF_SITE`, `CRAWLPROOF_AD_SLOT` | analytics and ads; `off` turns either off |
|
|
61
|
+
|
|
62
|
+
Production runs on dev2 under `bin/install.sh` (OpenInstall); every merge to main deploys. See `OpenStack.md`.
|
|
63
|
+
|
|
64
|
+
MIT. Part of the [Moshpit ring](https://pit.world/ring).
|
package/bin/builtindie
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profullstack/builtindie",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "built.indie: launch your indie product with one URL. We fetch the logo, screenshots, videos, llms.txt, OpenSite and OpenProfile from your homepage; people upvote; you get a badge. Web (PWA), API, CLI, TUI and MCP.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"homepage": "https://built.indie.pit.world",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/moshcoder/built.indie.git"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"builtindie": "bin/builtindie"
|
|
14
|
+
},
|
|
15
|
+
"files": ["bin/builtindie", "src", "public", "OpenStack.md", "README.md", "LICENSE"],
|
|
16
|
+
"engines": {
|
|
17
|
+
"bun": ">=1.1"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"start": "bun src/server.ts",
|
|
21
|
+
"dev": "bun --watch src/server.ts",
|
|
22
|
+
"test": "bun test",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.17.0",
|
|
27
|
+
"@profullstack/coinpay-onboard": "^0.1.1",
|
|
28
|
+
"@profullstack/hqtui": "^0.10.3",
|
|
29
|
+
"@profullstack/openprofile": "^0.2.0",
|
|
30
|
+
"hono": "^4.13.13",
|
|
31
|
+
"zod": "^3.25.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/bun": "^1.4.3",
|
|
35
|
+
"typescript": "^7.0.2"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/public/app.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// built.indie: vote without a page load, copy buttons, progress while we read a site, the service worker.
|
|
2
|
+
document.addEventListener('click', async (e) => {
|
|
3
|
+
const copy = e.target.closest('[data-copy]')
|
|
4
|
+
if (copy) {
|
|
5
|
+
e.preventDefault()
|
|
6
|
+
navigator.clipboard?.writeText(copy.dataset.copy).then(() => {
|
|
7
|
+
const was = copy.textContent
|
|
8
|
+
copy.textContent = 'Copied'
|
|
9
|
+
setTimeout(() => { copy.textContent = was }, 1500)
|
|
10
|
+
})
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
const vote = e.target.closest('[data-vote]')
|
|
14
|
+
if (!vote) return
|
|
15
|
+
e.preventDefault()
|
|
16
|
+
if (vote.classList.contains('voted')) return
|
|
17
|
+
vote.classList.add('voted', 'bump')
|
|
18
|
+
const count = vote.querySelector('.count')
|
|
19
|
+
count.textContent = String(Number(count.textContent) + 1)
|
|
20
|
+
try {
|
|
21
|
+
const r = await fetch('/p/' + encodeURIComponent(vote.dataset.vote) + '/vote', { method: 'POST', headers: { accept: 'application/json' } })
|
|
22
|
+
if (r.ok) count.textContent = String((await r.json()).votes)
|
|
23
|
+
} catch {}
|
|
24
|
+
})
|
|
25
|
+
document.addEventListener('submit', (e) => {
|
|
26
|
+
const form = e.target
|
|
27
|
+
if (form.dataset.confirm && !confirm(form.dataset.confirm)) return e.preventDefault()
|
|
28
|
+
if (!form.dataset.busy || form.classList.contains('busy')) return
|
|
29
|
+
form.classList.add('busy')
|
|
30
|
+
const note = document.createElement('p')
|
|
31
|
+
note.className = 'busy-note'
|
|
32
|
+
note.textContent = form.dataset.busy
|
|
33
|
+
form.append(note)
|
|
34
|
+
})
|
|
35
|
+
if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js').catch(() => {})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#0b0a10"/><path d="M32 9 11 35h14v20h14V35h14z" fill="#ff5c39"/></svg>
|
|
Binary file
|
|
Binary file
|