karpathytalk-community
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKarpathyTalk Community Skill
KarpathyTalk 社区Skill
Skill by ara.so — Daily 2026 Skills collection.
KarpathyTalk is a Go-based developer social network (Twitter × GitHub Gists) where posts are plain markdown, the social layer supports likes/reposts/follows/replies, and all data is openly accessible via JSON and markdown APIs — designed for both humans and LLM agents.
Skill由ara.so 出品 —— 2026每日技能合集。
KarpathyTalk是一个基于Go开发的开发者社交网络(结合了Twitter和GitHub Gists的特点),帖子为纯markdown格式,社交层支持点赞/转发/关注/回复,所有数据都可以通过JSON和markdown API公开访问——同时面向人类用户和LLM Agent设计。
What It Does
功能特性
- GitHub OAuth sign-in (no new credentials)
- Posts are GFM markdown with syntax-highlighted code blocks and image uploads
- Social features: likes, reposts, quote posts, replies, follows
- REST API returns JSON (for agents/code) or markdown (for humans)
- Single Go binary + SQLite + directory — trivial to self-host
uploads/ - Built with: Go, SQLite, htmx, goldmark
- GitHub OAuth登录(无需创建新账号)
- 帖子支持GFM markdown,可插入带语法高亮的代码块,支持图片上传
- 社交功能:点赞、转发、引用帖、回复、关注
- REST API可返回JSON(供Agent/代码调用)或markdown(供人类阅读)
- 仅单个Go二进制文件 + SQLite + 目录,自托管非常简单
uploads/ - 技术栈:Go、SQLite、htmx、goldmark
Installation & Local Setup
安装与本地部署
1. Create a GitHub OAuth App
1. 创建GitHub OAuth应用
Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App:
| Field | Value |
|---|---|
| Application name | KarpathyTalk |
| Homepage URL | |
| Authorization callback URL | |
Save the Client ID and Client Secret.
前往 GitHub → 设置 → 开发者设置 → OAuth Apps → 新建OAuth App:
| 字段 | 值 |
|---|---|
| 应用名称 | KarpathyTalk |
| 主页URL | |
| 授权回调URL | |
保存生成的Client ID和Client Secret。
2. Clone & Build
2. 克隆仓库并构建
bash
git clone https://github.com/karpathy/KarpathyTalk.git
cd KarpathyTalk
go build -o karpathytalk ./cmd/karpathytalkbash
git clone https://github.com/karpathy/KarpathyTalk.git
cd KarpathyTalk
go build -o karpathytalk ./cmd/karpathytalk3. Configure Environment
3. 配置环境变量
bash
export GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
export GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
export BASE_URL=http://localhost:8080 # optional, defaults to thisbash
export GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
export GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
export BASE_URL=http://localhost:8080 # 可选,默认值为该地址4. Run
4. 运行
bash
./karpathytalkbash
./karpathytalkor with options:
或自定义参数运行:
./karpathytalk -addr :9090 -db ./data/karpathytalk.db
Visit `http://localhost:8080`.
---./karpathytalk -addr :9090 -db ./data/karpathytalk.db
访问 `http://localhost:8080` 即可使用。
---CLI Flags
命令行参数
-addr string HTTP listen address (default ":8080")
-db string SQLite database path (default "karpathytalk.db")-addr string HTTP监听地址(默认值 ":8080")
-db string SQLite数据库路径(默认值 "karpathytalk.db")Environment Variables
环境变量
| Variable | Required | Default | Description |
|---|---|---|---|
| ✅ | — | GitHub OAuth client ID |
| ✅ | — | GitHub OAuth client secret |
| ❌ | | Public URL of the deployed app |
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| ✅ | — | GitHub OAuth客户端ID |
| ✅ | — | GitHub OAuth客户端密钥 |
| ❌ | | 部署后应用的公网URL |
Deployment (Production)
生产环境部署
Build & Copy
构建并拷贝文件
bash
undefinedbash
undefinedBuild binary
构建二进制文件
go build -o karpathytalk ./cmd/karpathytalk
go build -o karpathytalk ./cmd/karpathytalk
Copy to server (adjust user/host)
拷贝到服务器(请替换对应的用户/主机地址)
scp karpathytalk schema.sql user@yourserver:/karpathytalk/
scp -r templates static user@yourserver:/karpathytalk/
undefinedscp karpathytalk schema.sql user@yourserver:/karpathytalk/
scp -r templates static user@yourserver:/karpathytalk/
undefinedRun on Server
在服务器上运行
bash
ssh user@yourserver
cd ~/karpathytalk
export GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
export GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
export BASE_URL=https://yourdomain.com
./karpathytalk -addr :8080bash
ssh user@yourserver
cd ~/karpathytalk
export GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
export GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
export BASE_URL=https://yourdomain.com
./karpathytalk -addr :8080Caddy TLS (recommended)
Caddy TLS配置(推荐)
caddyfile
yourdomain.com {
reverse_proxy localhost:8080
}caddyfile
yourdomain.com {
reverse_proxy localhost:8080
}nginx TLS
nginx TLS配置
nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}systemd Service
systemd服务配置
ini
[Unit]
Description=KarpathyTalk
After=network.target
[Service]
WorkingDirectory=/home/deploy/karpathytalk
ExecStart=/home/deploy/karpathytalk/karpathytalk -addr :8080
Restart=always
Environment=GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
Environment=GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
Environment=BASE_URL=https://yourdomain.com
[Install]
WantedBy=multi-user.targetini
[Unit]
Description=KarpathyTalk
After=network.target
[Service]
WorkingDirectory=/home/deploy/karpathytalk
ExecStart=/home/deploy/karpathytalk/karpathytalk -addr :8080
Restart=always
Environment=GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
Environment=GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
Environment=BASE_URL=https://yourdomain.com
[Install]
WantedBy=multi-user.targetAPI Usage
API 使用说明
All data is open — no auth required for reads. The API returns JSON for programmatic access and markdown for human/agent reading.
所有数据都是公开的——读操作无需授权。API可返回JSON供程序调用,也可返回markdown供人类/Agent阅读。
Fetch Posts as JSON
以JSON格式获取帖子
bash
undefinedbash
undefinedTimeline / recent posts
时间线/最新帖子
Single post
单条帖子
User's posts
指定用户的帖子
undefinedundefinedFetch Posts as Markdown
以Markdown格式获取帖子
bash
undefinedbash
undefinedHuman/agent-readable markdown
人类/Agent可读的markdown格式
undefinedundefinedGo Agent Example — Read Timeline
Go Agent示例 — 读取时间线
go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type Post struct {
ID int64 `json:"id"`
Username string `json:"username"`
Content string `json:"content"`
Likes int `json:"likes"`
Reposts int `json:"reposts"`
CreatedAt string `json:"created_at"`
}
func fetchTimeline(baseURL string) ([]Post, error) {
resp, err := http.Get(baseURL + "/api/posts")
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var posts []Post
if err := json.Unmarshal(body, &posts); err != nil {
return nil, err
}
return posts, nil
}
func main() {
posts, err := fetchTimeline("https://karpathytalk.com")
if err != nil {
panic(err)
}
for _, p := range posts {
fmt.Printf("[%s] %s (👍 %d)\n", p.Username, p.Content[:min(80, len(p.Content))], p.Likes)
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type Post struct {
ID int64 `json:"id"`
Username string `json:"username"`
Content string `json:"content"`
Likes int `json:"likes"`
Reposts int `json:"reposts"`
CreatedAt string `json:"created_at"`
}
func fetchTimeline(baseURL string) ([]Post, error) {
resp, err := http.Get(baseURL + "/api/posts")
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var posts []Post
if err := json.Unmarshal(body, &posts); err != nil {
return nil, err
}
return posts, nil
}
func main() {
posts, err := fetchTimeline("https://karpathytalk.com")
if err != nil {
panic(err)
}
for _, p := range posts {
fmt.Printf("[%s] %s (👍 %d)\n", p.Username, p.Content[:min(80, len(p.Content))], p.Likes)
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}Go Agent Example — Fetch User Posts as Markdown
Go Agent示例 — 以Markdown格式获取用户帖子
go
package main
import (
"fmt"
"io"
"net/http"
)
func fetchUserPostsMarkdown(baseURL, username string) (string, error) {
url := fmt.Sprintf("%s/api/users/%s/posts.md", baseURL, username)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
md, err := fetchUserPostsMarkdown("https://karpathytalk.com", "karpathy")
if err != nil {
panic(err)
}
fmt.Println(md)
}go
package main
import (
"fmt"
"io"
"net/http"
)
func fetchUserPostsMarkdown(baseURL, username string) (string, error) {
url := fmt.Sprintf("%s/api/users/%s/posts.md", baseURL, username)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
md, err := fetchUserPostsMarkdown("https://karpathytalk.com", "karpathy")
if err != nil {
panic(err)
}
fmt.Println(md)
}Content Limits
内容限制
| Content Type | Max Length | Rate Limit |
|---|---|---|
| Posts | 10,000 characters | 30 per hour |
| Replies | 5,000 characters | 60 per hour |
| Images | 5 MB | PNG/JPEG/GIF/WebP only |
| 内容类型 | 最大长度 | 频率限制 |
|---|---|---|
| 帖子 | 10000字符 | 每小时30条 |
| 回复 | 5000字符 | 每小时60条 |
| 图片 | 5 MB | 仅支持PNG/JPEG/GIF/WebP格式 |
Database — Direct SQLite Access
数据库 - 直接访问SQLite
The SQLite database is a single file. You can query it directly for analytics, backups, or migrations:
bash
undefinedSQLite数据库是单个文件,你可以直接查询数据用于分析、备份或迁移:
bash
undefinedOpen database
打开数据库
sqlite3 karpathytalk.db
sqlite3 karpathytalk.db
List tables
列出所有表
.tables
.tables
Recent posts
最新帖子
SELECT username, substr(content, 1, 80), created_at
FROM posts
ORDER BY created_at DESC
LIMIT 20;
SELECT username, substr(content, 1, 80), created_at
FROM posts
ORDER BY created_at DESC
LIMIT 20;
Most liked posts
点赞最多的帖子
SELECT username, likes, substr(content, 1, 60)
FROM posts
ORDER BY likes DESC
LIMIT 10;
SELECT username, likes, substr(content, 1, 60)
FROM posts
ORDER BY likes DESC
LIMIT 10;
User follower counts
用户粉丝数排行
SELECT username, COUNT(*) as followers
FROM follows
GROUP BY username
ORDER BY followers DESC;
undefinedSELECT username, COUNT(*) as followers
FROM follows
GROUP BY username
ORDER BY followers DESC;
undefinedBackup
备份
bash
undefinedbash
undefinedSimple file copy (safe while running with WAL mode)
简单文件拷贝(开启WAL模式时运行中拷贝也安全)
cp karpathytalk.db karpathytalk.db.backup
cp karpathytalk.db karpathytalk.db.backup
Or use sqlite3 online backup
或使用sqlite3在线备份功能
sqlite3 karpathytalk.db ".backup karpathytalk_backup.db"
---sqlite3 karpathytalk.db ".backup karpathytalk_backup.db"
---Project Structure
项目结构
KarpathyTalk/
├── cmd/
│ └── karpathytalk/ # main entrypoint
├── templates/ # HTML templates (htmx-powered)
├── static/ # CSS, JS assets
├── uploads/ # User image uploads
├── schema.sql # SQLite schema
└── karpathytalk.db # Database (created at runtime)KarpathyTalk/
├── cmd/
│ └── karpathytalk/ # 主程序入口
├── templates/ # HTML模板(基于htmx)
├── static/ # CSS、JS静态资源
├── uploads/ # 用户上传的图片存储目录
├── schema.sql # SQLite表结构
└── karpathytalk.db # 数据库文件(运行时自动创建)Common Patterns
常用使用场景
Pattern: Agent That Monitors New Posts
场景:监控新帖子的Agent
go
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type Post struct {
ID int64 `json:"id"`
Username string `json:"username"`
Content string `json:"content"`
CreatedAt string `json:"created_at"`
}
func pollNewPosts(baseURL string, sinceID int64) ([]Post, error) {
url := fmt.Sprintf("%s/api/posts?since_id=%d", baseURL, sinceID)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var posts []Post
json.NewDecoder(resp.Body).Decode(&posts)
return posts, nil
}
func main() {
var lastSeenID int64 = 0
for {
posts, err := pollNewPosts("https://karpathytalk.com", lastSeenID)
if err != nil {
fmt.Println("Error:", err)
} else {
for _, p := range posts {
fmt.Printf("New post by @%s: %s\n", p.Username, p.Content[:min(60, len(p.Content))])
if p.ID > lastSeenID {
lastSeenID = p.ID
}
}
}
time.Sleep(30 * time.Second)
}
}
func min(a, b int) int {
if a < b { return a }
return b
}go
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type Post struct {
ID int64 `json:"id"`
Username string `json:"username"`
Content string `json:"content"`
CreatedAt string `json:"created_at"`
}
func pollNewPosts(baseURL string, sinceID int64) ([]Post, error) {
url := fmt.Sprintf("%s/api/posts?since_id=%d", baseURL, sinceID)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var posts []Post
json.NewDecoder(resp.Body).Decode(&posts)
return posts, nil
}
func main() {
var lastSeenID int64 = 0
for {
posts, err := pollNewPosts("https://karpathytalk.com", lastSeenID)
if err != nil {
fmt.Println("Error:", err)
} else {
for _, p := range posts {
fmt.Printf("New post by @%s: %s\n", p.Username, p.Content[:min(60, len(p.Content))])
if p.ID > lastSeenID {
lastSeenID = p.ID
}
}
}
time.Sleep(30 * time.Second)
}
}
func min(a, b int) int {
if a < b { return a }
return b
}Pattern: Embedding KarpathyTalk Content in an LLM Prompt
场景:将KarpathyTalk内容嵌入LLM提示词
go
package main
import (
"fmt"
"io"
"net/http"
)
// Fetch markdown content to inject into an LLM system prompt or context window
func getContextForLLM(baseURL, username string) (string, error) {
url := fmt.Sprintf("%s/api/users/%s/posts.md", baseURL, username)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
context, _ := getContextForLLM("https://karpathytalk.com", "karpathy")
systemPrompt := fmt.Sprintf(`You are a helpful assistant. Here are recent posts from the community:
%s
Answer questions about these posts.`, context)
fmt.Println(systemPrompt)
}go
package main
import (
"fmt"
"io"
"net/http"
)
// 获取markdown内容注入到LLM系统提示词或上下文窗口中
func getContextForLLM(baseURL, username string) (string, error) {
url := fmt.Sprintf("%s/api/users/%s/posts.md", baseURL, username)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
context, _ := getContextForLLM("https://karpathytalk.com", "karpathy")
systemPrompt := fmt.Sprintf(`You are a helpful assistant. Here are recent posts from the community:
%s
Answer questions about these posts.`, context)
fmt.Println(systemPrompt)
}Troubleshooting
故障排查
| Problem | Cause | Fix |
|---|---|---|
| Missing env var | Export |
| OAuth callback fails | Callback URL mismatch | Ensure GitHub OAuth App callback URL exactly matches |
| Binary not found after build | Wrong output path | Run |
| Missing templates dir | Run the binary from the repo root, or copy |
| Database locked | Concurrent writers | SQLite WAL mode is set by default; avoid multiple binary instances against same |
| Images not serving | Missing | The directory is created automatically on first upload; check write permissions |
| Port already in use | Another process on 8080 | Use |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 缺少环境变量 | 运行前先导出 |
| OAuth回调失败 | 回调URL不匹配 | 确保GitHub OAuth应用的回调URL与 |
| 构建后找不到二进制文件 | 输出路径错误 | 从仓库根目录执行 |
| 缺少templates目录 | 从仓库根目录运行二进制文件,或者将 |
| 数据库锁定 | 并发写入冲突 | 默认已开启SQLite WAL模式;避免多个二进制实例操作同一个 |
| 图片无法加载 | 缺少 | 首次上传时会自动创建该目录;请检查目录写入权限 |
| 端口已被占用 | 8080端口被其他进程占用 | 使用 |
Check the App is Running
检查应用是否正常运行
bash
curl -I http://localhost:8080/bash
curl -I http://localhost:8080/Expect: HTTP/1.1 200 OK
预期输出:HTTP/1.1 200 OK
undefinedundefinedRebuild After Code Changes
代码变更后重新构建
bash
go build -o karpathytalk ./cmd/karpathytalk && ./karpathytalkbash
go build -o karpathytalk ./cmd/karpathytalk && ./karpathytalkRun Tests
运行测试
bash
go test ./...bash
go test ./...