diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..cf60ada
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,6 @@
+PORT=3000
+DUCKDB_PATH=./data/awesome.duckdb
+SESSION_SECRET=change-me-in-production
+ADMIN_INIT_PASSWORD=admin
+AI_INGEST_KEY=
+BASE_URL=http://localhost:3000
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..97725b4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+data/
+.env
+*.log
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..eaea0ed
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+FROM node:22-bookworm-slim AS deps
+WORKDIR /app
+COPY package.json package-lock.json ./
+RUN npm ci --omit=dev --no-audit --no-fund
+
+FROM node:22-bookworm-slim
+WORKDIR /app
+ENV NODE_ENV=production
+COPY --from=deps /app/node_modules ./node_modules
+COPY package.json ./
+COPY src ./src
+COPY public ./public
+VOLUME ["/app/data"]
+EXPOSE 3000
+USER node
+HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \
+ CMD node -e "fetch('http://localhost:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
+CMD ["node", "src/index.js"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8bc3c26
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+# Awesome Index
+
+把网上牛逼的东西收进一个库:给人用,也给 AI 用。
+精选开源软件 / 微服务 / SaaS / 网站 / 工具 / 脚本 / 插件的结构化数据库,内置 MCP 接口。
+
+- 设计方案:[design/DESIGN.md](design/DESIGN.md)(Riso 印刷风格 + 静态原型)
+- 框架设计:[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)(模块划分 / 数据模型 / 端点)
+
+## 快速开始
+
+### 本地开发
+
+```bash
+npm install
+npm run dev # http://localhost:3000(--watch 热重载)
+npm test # node:test,10 个用例
+```
+
+首次启动自动完成建表与种子数据。默认管理员 `admin`,密码取 `ADMIN_INIT_PASSWORD`(默认 `admin`)。
+
+### Docker 部署
+
+```bash
+cp .env.example .env # 修改 SESSION_SECRET / ADMIN_INIT_PASSWORD / AI_INGEST_KEY
+docker compose up -d --build
+```
+
+数据持久化在宿主机 `./data/awesome.duckdb`,升级镜像不丢数据。
+
+## 给人的入口
+
+| 页面 | 地址 |
+|---|---|
+| 首页(搜索 / MCP 说明 / 最近收录) | `/` |
+| 列表页(类型 + 嵌套标签过滤) | `/entries?q=关键字&type=工具&tags=1,2&sort=stars` |
+| 详情页(元数据 / 评论 / 机读 JSON) | `/entries/:slug` |
+| 随机游览 | `/random` |
+| 登录 | `/login` |
+| 管理台(内容/标签/评论/内容源/账号) | `/admin` |
+
+## 给 AI 的入口(MCP)
+
+Streamable HTTP · 只读 · 无需密钥:
+
+```
+POST {BASE_URL}/mcp
+tools: search_entries / get_entry / random_entry / list_tags
+```
+
+一句话让 Agent 自己接入:
+
+> 请把 URL 为 `{BASE_URL}/mcp` 的 MCP 服务器添加到你的客户端配置中,名称用 awesome-index,完成后告诉我现在可以调用哪些工具。
+
+## 内容源(自动化收录)
+
+三类来源汇聚到同一条目表:
+
+| kind | 说明 | 新条目状态 |
+|---|---|---|
+| `human` | 管理台人工录入(常开) | active |
+| `ai` | `POST /api/ingest/entry` + `X-Ingest-Key` 头 | pending |
+| `feed` | 定时拉取外部站点(rss / html / eryajf-weekly 适配器),管理台可手动触发 | pending |
+
+首个站点适配器:二丫讲梵学习周刊(wiki.eryajf.net)。运行记录见管理台「内容源 → 运行记录」。
+
+## 技术栈
+
+Node.js 22 (ESM) · Express 5 · EJS · DuckDB (`@duckdb/node-api`) · zod · express-session · @modelcontextprotocol/sdk · node-cron · cheerio
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..f461aaf
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,16 @@
+services:
+ web:
+ build: .
+ container_name: awesome-index
+ ports:
+ - "3000:3000"
+ volumes:
+ - ./data:/app/data
+ environment:
+ PORT: "3000"
+ DUCKDB_PATH: /app/data/awesome.duckdb
+ SESSION_SECRET: ${SESSION_SECRET:-change-me-in-production}
+ ADMIN_INIT_PASSWORD: ${ADMIN_INIT_PASSWORD:-admin}
+ AI_INGEST_KEY: ${AI_INGEST_KEY:-}
+ BASE_URL: ${BASE_URL:-http://localhost:3000}
+ restart: unless-stopped
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
new file mode 100644
index 0000000..43c15ff
--- /dev/null
+++ b/docs/ARCHITECTURE.md
@@ -0,0 +1,270 @@
+# Awesome Index · 框架设计
+
+> 目标:把 `design/prototype/` 的静态原型落地为一个由 DuckDB 驱动、全 JavaScript 编写、
+> 对人与对 AI(MCP)同时提供服务、可 Docker 部署的小型数据库网站。
+> 本文档回答三个问题:**模块怎么分、每个模块负责什么、模块对应哪个目录。**
+
+---
+
+## 1. 总体架构
+
+经典三横层,外加一个并列的机器读者入口:
+
+```
+ ┌──────────────────────────────────────────┐
+ 人类读者 ────▶ │ 视图层 views/ (EJS SSR) + public/ 静态 │
+ │ 页面内
+<%- include('partials/footer-bar') %>
diff --git a/src/views/detail.ejs b/src/views/detail.ejs
new file mode 100644
index 0000000..2e25292
--- /dev/null
+++ b/src/views/detail.ejs
@@ -0,0 +1,96 @@
+<%- include('partials/head') %>
+<%- include('partials/topbar', { active: 'browse' }) %>
+
+
+ ~/awesome-index / <%= entry.type %> / <%= entry.title %>
+
+
+
+
<%= entry.title %>
+
<%= entry.type %>
+ <% if (entry.status === 'pending') { %>
pending 待核验<% } %>
+
+
+
<%= entry.description_md %>
+
+
+
+
+
+ // entry.body · markdown source
+ 这是什么
+ <%= entry.description_md || '暂无介绍。' %>
+ 仓库地址:<%= entry.url %>
+
+
+
+ // dual reader view · 人读 ⇄ 机读
+
+
+
+
+
+
这一页就是人读视图。切换到「机读 JSON」可以看到 AI 通过 MCP 的 get_entry 拿到的同一条数据——两种读者,同一份事实。
+
+
+
+
+
+ // comments · <%= comments.length %> 条
+ 评论
+ <% for (const c of comments) { %>
+
+ <% } %>
+ <% if (!comments.length) { %>还没有评论,说点有用的。
<% } %>
+
+
+
+
+
+
+
+
+
+<%- include('partials/footer-bar') %>
diff --git a/src/views/index.ejs b/src/views/index.ejs
new file mode 100644
index 0000000..4366138
--- /dev/null
+++ b/src/views/index.ejs
@@ -0,0 +1,146 @@
+<%- include('partials/head') %>
+<%- include('partials/topbar', { active: 'home' }) %>
+
+
+
+
+
+
~/awesome-index — 给人用,也给 AI 用
+
把网上牛逼的东西,
收进一个好用的库。
+
人工精选的开源软件、微服务、SaaS、网站、工具、脚本与插件。每一条都是结构化数据:人在页面上搜,AI 通过 MCP 直接调用。
+
+
+
+
+
+
+ 给 AI 用?本站提供 MCP 接口
+
+
+
+
+ 收录 <%= stats.total %> 条
+ 在架 <%= stats.active %> 条
+ 标签组 <%= stats.tags %> 个
+ 本周新增 +<%= stats.weekNew %>
+ 存储 DuckDB
+ MCP 就绪
+
+
+
+
+
// get_entry("ripgrep") · 同一条数据,两种读者
+
{
+ "id": "ripgrep",
+ "type": "tool",
+ "name": "ripgrep",
+ "tags": ["Rust", "CLI"],
+ "stars": <%= typeof recent[0] !== 'undefined' ? Number(recent[0].stars) : 52300 %>,
+ "status": "active"
+}
+
+
工具
+
ripgrep
+
以正则递归搜索目录,默认尊重 .gitignore,是代码库里最快的找东西方式。
+
RustCLI
+
★ <%= fmtStars(52300) %> · 终端找东西的事实标准
+
+
+
+
+
+
+
+
+
// mcp.transport: streamable-http · auth: none
+
同一个库,两种读者。
AI 走这边。
+
本站所有条目同时暴露为 MCP(Model Context Protocol)接口。把下面的地址加进你的 AI 客户端,Claude、Cursor 或任何支持 MCP 的 Agent 就能直接搜索与读取这个库。
+
+
+
+
不想手动改配置?
+
复制下面这句话发给你的 Agent,它会自己完成接入:
+
「请把 URL 为 <%- config.baseUrl %>/mcp 的 MCP 服务器添加到你的客户端配置中,名称用 awesome-index,完成后告诉我现在可以调用哪些工具。」
+
+
+
+
+
+
+
+
+
+
01
把服务器加进客户端配置
+
{
+ "mcpServers": {
+ "awesome-index": {
+ "url": "<%- config.baseUrl %>/mcp"
+ }
+ }
+}
+
+
+
+
03
直接问你的 AI
+
「用 awesome-index 找三个能自托管的相册方案,
+ 按 star 数排序,给我 repo 链接。」
+
+
+
+
+
+
+
+
+
+
// resource: recent_entries · limit 4
+
最近收录
+
+
查看全部 →
+
+
+
+
+
+<%- include('partials/footer-full') %>
diff --git a/src/views/list.ejs b/src/views/list.ejs
new file mode 100644
index 0000000..eeea107
--- /dev/null
+++ b/src/views/list.ejs
@@ -0,0 +1,115 @@
+<%- include('partials/head') %>
+<%- include('partials/topbar', { active: 'browse' }) %>
+
+
+
+
// tool: search_entries · filters applied server-side
+
<%= filters.q ? '「' + filters.q + '」的搜索结果' : '浏览全部' %>
+
+
+
+
+
+
+
+
+
+
+<%- include('partials/footer-bar') %>
diff --git a/src/views/login.ejs b/src/views/login.ejs
new file mode 100644
index 0000000..12aed4b
--- /dev/null
+++ b/src/views/login.ejs
@@ -0,0 +1,42 @@
+<%- include('partials/head') %>
+<%- include('partials/topbar', { active: 'admin' }) %>
+
+
+
+
+ // route: /admin/login · auth: session cookie
+
+ 管理台登录
+ 人工通道。AI Agent 请走 MCP 接口——这扇门不对机器开放。
+
+
+
+
+ // 初始账号:admin(密码为 ADMIN_INIT_PASSWORD)
+ // agents: POST /mcp · 此处返回 403
+
+
+
+
+
+
+<%- include('partials/footer-bar') %>
diff --git a/src/views/partials/footer-bar.ejs b/src/views/partials/footer-bar.ejs
new file mode 100644
index 0000000..183a498
--- /dev/null
+++ b/src/views/partials/footer-bar.ejs
@@ -0,0 +1,11 @@
+
+
+
+
+
<%= c.body %>
+