尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A trust-ranked classifieds marketplace for Philippine province users, built on
TrustClub.
Members post things for sale, things for rent, and services they offer. Other
members browse them ranked by their own trust path to the poster, filtered
by category-specific attributes, price and location. There is exactly one call
to action on a listing: contact the seller.
KantoList deliberately does not handle payment, escrow, delivery or messaging.
Its job is discovery and trust signalling; the transaction happens between two
people who now have a reason to trust each other.檢視完整 README ↗A trust-ranked classifieds marketplace for Philippine province users, built on TrustClub.
Members post things for sale, things for rent, and services they offer. Other members browse them ranked by their own trust path to the poster, filtered by category-specific attributes, price and location. There is exactly one call to action on a listing: contact the seller.
KantoList deliberately does not handle payment, escrow, delivery or messaging. Its job is discovery and trust signalling; the transaction happens between two people who now have a reason to trust each other.
Kanto is the family; KantoList is the goods market. KantoRooms,
KantoServices, KantoFarm and KantoJobs are planned beside it, sharing one
account, one login and one trust lookup. The market switcher in the header is
already wired for them.
cp .env.example .env # fill in DATABASE_URL and AUTH_SECRET at minimum
npm install
npx prisma migrate dev # applies migrations and seeds
npm run dev
Without TrustClub credentials the OIDC login cannot run. For local work set
ALLOW_DEV_LOGIN=true and the sign-in page offers the seeded accounts —
juan.santos has a pre-warmed trust cache, so trust ranking is visible
immediately.
| Command | What it does |
|---|---|
npm run dev | Dev server |
npm run build | prisma generate → migrate deploy → next build |
npm test | Jest unit tests |
npm run lint / npm run typecheck | ESLint / tsc --noEmit |
npm run seed | Re-seed for development (destructive — see below) |
npm run seed:reference | Categories and municipalities only; additive, safe against production |
npm run seed:demo | 77 realistic demo listings across 24 accounts; idempotent, adds only |
| Path | What lives there |
|---|---|
src/lib/trustclub.ts | Trust-chain lookups, cached, with a batched per-viewer call |
src/lib/trustclub-oidc.ts | The OIDC device-authorization wire protocol |
src/lib/search.ts | Search, filtering and the trust ranking pipeline |
src/lib/attributes.ts | The category-attribute engine |
src/lib/listing.ts | Listing rules: codes, slugs, expiry, price units |
src/components/ui.tsx | The signwriter primitives |
src/app/api/* | Route handlers |
Trust points are directed: TrustClub answers "how strongly does A's network vouch for B", not "how trusted is B". There is no global score, so ranking is per viewer and cannot be precomputed into an index column.
searchListings therefore: narrows hard in SQL → takes a bounded candidate
window ordered by recency → resolves trust for that window only, batched and
budgeted → sorts and paginates. The tradeoff: a listing outside the
candidate window cannot be lifted by trust however strong it is. The window is
several pages deep, so this only bites on very broad searches.
Three states are kept distinct everywhere, and a failed lookup is never rendered as a zero:
| State | Meaning | Shown as |
|---|---|---|
| a number | the viewer's network vouches for the poster | green badge, 320 TP · via @juan |
0 | the graph has no path between them | grey "No trust path" |
null | the lookup failed or was never made | grey "Trust unknown" |
The badge reuses TruRate's indirect_trust_incoming / outgoing symbols and
its formatTrustPoints abbreviations, so the number means the same thing to a
member who uses both products.
A leaf category carries an attributeSchema — a JSON array of attribute
definitions. It drives the posting form, the filter rail and the detail table,
and is validated on write. Adding a filter is a seed edit, not a deploy.
{ "key": "displacement_cc", "label": "Engine size", "type": "int",
"unit": "cc", "min": 25, "max": 2000, "filter": "range",
"buckets": [{ "label": "125–155", "min": 125, "max": 155 }] }
Production runs prisma migrate deploy, which applies migration files, not
the live schema. A schema edit without a matching migration means the deployed
client references a column the database never got. Always:
npx prisma migrate dev --name <change_name>
npx prisma migrate diff --from-schema-datasource prisma/schema.prisma \
--to-schema-datamodel prisma/schema.prisma # must print "No difference detected."
Seeding is destructive and refuses to run against a non-localhost
DATABASE_URL unless ALLOW_DESTRUCTIVE_SEED=1 is set.
Filipino vernacular signage — hand-painted jeepney lettering, sari-sari store price boards. Flat enamel fills and hard black outlines survive a cheap LCD in daylight, which is where province users actually shop, better than gradients and soft shadows do.
Cream #FFF4D6 ground, enamel red #C6362B, chrome yellow #F2B01E, jeepney
green #1F6B4A for trust. Square corners; one depth device (4px 4px 0
offset); Anton for display and Barlow Condensed for labels. Yellow carries
black text only. Tokens live in src/app/globals.css; compose
src/components/ui.tsx rather than re-typing the rules.
See docs/DEPLOY.md. In short: create the Postgres first
(the build runs prisma migrate deploy), set DATABASE_URL and AUTH_SECRET,
import the repo in Vercel, then seed the category tree.
phoneVerifiedAt exists and gates the
larger posting allowance, but nothing sets it yet.expiresAt is set and filtered on, but nothing flips
ACTIVE to EXPIRED on a schedule.en/tl layer yet./terms and /privacy carry visible placeholders pending legal review.