karpathytalk-community

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

KarpathyTalk 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 +
    uploads/
    directory — trivial to self-host
  • 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:
FieldValue
Application nameKarpathyTalk
Homepage URL
http://localhost:8080
Authorization callback URL
http://localhost:8080/auth/callback
Save the Client ID and Client Secret.
前往 GitHub → 设置 → 开发者设置 → OAuth Apps → 新建OAuth App
字段
应用名称KarpathyTalk
主页URL
http://localhost:8080
授权回调URL
http://localhost:8080/auth/callback
保存生成的Client IDClient Secret

2. Clone & Build

2. 克隆仓库并构建

bash
git clone https://github.com/karpathy/KarpathyTalk.git
cd KarpathyTalk
go build -o karpathytalk ./cmd/karpathytalk
bash
git clone https://github.com/karpathy/KarpathyTalk.git
cd KarpathyTalk
go build -o karpathytalk ./cmd/karpathytalk

3. 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 this
bash
export GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
export GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
export BASE_URL=http://localhost:8080   # 可选,默认值为该地址

4. Run

4. 运行

bash
./karpathytalk
bash
./karpathytalk

or 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

环境变量

VariableRequiredDefaultDescription
GITHUB_CLIENT_ID
GitHub OAuth client ID
GITHUB_CLIENT_SECRET
GitHub OAuth client secret
BASE_URL
http://localhost:8080
Public URL of the deployed app

变量必填默认值说明
GITHUB_CLIENT_ID
GitHub OAuth客户端ID
GITHUB_CLIENT_SECRET
GitHub OAuth客户端密钥
BASE_URL
http://localhost:8080
部署后应用的公网URL

Deployment (Production)

生产环境部署

Build & Copy

构建并拷贝文件

bash
undefined
bash
undefined

Build 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/
undefined
scp karpathytalk schema.sql user@yourserver:/karpathytalk/ scp -r templates static user@yourserver:/karpathytalk/
undefined

Run 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 :8080
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 :8080

Caddy 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.target

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.target

API 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
undefined
bash
undefined

Timeline / recent posts

时间线/最新帖子

Single post

单条帖子

User's posts

指定用户的帖子

Fetch Posts as Markdown

以Markdown格式获取帖子

bash
undefined
bash
undefined

Human/agent-readable markdown

人类/Agent可读的markdown格式

Go 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 TypeMax LengthRate Limit
Posts10,000 characters30 per hour
Replies5,000 characters60 per hour
Images5 MBPNG/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
undefined
SQLite数据库是单个文件,你可以直接查询数据用于分析、备份或迁移:
bash
undefined

Open 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;
undefined
SELECT username, COUNT(*) as followers FROM follows GROUP BY username ORDER BY followers DESC;
undefined

Backup

备份

bash
undefined
bash
undefined

Simple 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

故障排查

ProblemCauseFix
GITHUB_CLIENT_ID not set
Missing env varExport
GITHUB_CLIENT_ID
before running
OAuth callback failsCallback URL mismatchEnsure GitHub OAuth App callback URL exactly matches
BASE_URL/auth/callback
Binary not found after buildWrong output pathRun
go build -o karpathytalk ./cmd/karpathytalk
from repo root
templates: no such file
Missing templates dirRun the binary from the repo root, or copy
templates/
and
static/
next to the binary
Database lockedConcurrent writersSQLite WAL mode is set by default; avoid multiple binary instances against same
.db
Images not servingMissing
uploads/
dir
The directory is created automatically on first upload; check write permissions
Port already in useAnother process on 8080Use
-addr :9090
to change the port
问题原因解决方案
GITHUB_CLIENT_ID not set
缺少环境变量运行前先导出
GITHUB_CLIENT_ID
环境变量
OAuth回调失败回调URL不匹配确保GitHub OAuth应用的回调URL与
BASE_URL/auth/callback
完全一致
构建后找不到二进制文件输出路径错误从仓库根目录执行
go build -o karpathytalk ./cmd/karpathytalk
templates: no such file
缺少templates目录从仓库根目录运行二进制文件,或者将
templates/
static/
目录拷贝到二进制文件同级目录
数据库锁定并发写入冲突默认已开启SQLite WAL模式;避免多个二进制实例操作同一个
.db
文件
图片无法加载缺少
uploads/
目录
首次上传时会自动创建该目录;请检查目录写入权限
端口已被占用8080端口被其他进程占用使用
-addr :9090
参数修改监听端口

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

undefined
undefined

Rebuild After Code Changes

代码变更后重新构建

bash
go build -o karpathytalk ./cmd/karpathytalk && ./karpathytalk
bash
go build -o karpathytalk ./cmd/karpathytalk && ./karpathytalk

Run Tests

运行测试

bash
go test ./...
bash
go test ./...