asopi tech
asopi techIndie Developer

Asopitech With · Read later

Instapaper with Alopex DB

Instapaper is a service that saves web articles to read later, offline too. How would we make it with Alopex DB?

Updated: Oct 12, 2026

What the service does

Instapaper saves articles found on the web to read later. Saved articles sync across devices and can be read offline. Readers can highlight passages and add notes. The data is article text, highlights, notes and folders.

Where the data lives

  • SQL tables: articles, folders, highlights and notes live in tables. The reading position is kept on the article row.
  • Full-text search: a full-text index covers article text and highlights.
  • Vectors: article embeddings in a vector column, with an HNSW index, bring up articles close to the one being read.

The app fetches the articles and computes the embeddings.

Deployment

Alopex DB is embedded in the app on the device. Articles live in one file on the device, so they can be read on the train. Sync between devices goes through an Alopex DB server.

Why Alopex DB fits

Saved articles get harder to find as they pile up. Alopex DB runs full-text search and similar-article search in the same SQL, and turns the pile into the next thing to read.

See it in code

Articles hold body, embedding and highlights, and the article you are reading surfaces nearby ones. Just the core of working through a backlog.

import alopex
import numpy as np

db = alopex.Database.open("instapaper.alopex")

# 記事(本文+埋め込み)とハイライトを持つ
db.execute_sql("""
    CREATE TABLE IF NOT EXISTS articles (
        id INTEGER PRIMARY KEY,
        title TEXT,
        body TEXT,
        embedding VECTOR(4)
    )
""")
db.execute_sql("""
    CREATE TABLE IF NOT EXISTS highlights (
        article_id INTEGER,
        quote TEXT
    )
""")

articles = [
    (1, "Rustの非同期入門", "async/awaitの基礎", [0.9, 0.1, 0.0, 0.0]),
    (2, "確定申告の基礎", "控除のまとめ", [0.0, 0.0, 0.9, 0.1]),
    (3, "Tokioの仕組み", "ランタイムの話", [0.85, 0.15, 0.0, 0.0]),
]
for aid, title, body, emb in articles:
    db.execute_sql(
        "INSERT INTO articles (id, title, body, embedding) VALUES (?, ?, ?, ?)",
        [aid, title, body, np.array(emb, dtype=np.float32)],
    )
db.execute_sql("INSERT INTO highlights (article_id, quote) VALUES (?, ?)", [1, "awaitは評価を止めない"])

# いま読んでいる記事(Rustの非同期入門)の埋め込みに近い、ほかの記事を出す
reading = np.array([0.9, 0.1, 0.0, 0.0], dtype=np.float32)
rows = db.execute_sql(
    """
    SELECT title, vector_similarity(embedding, ?, 'cosine') AS score
    FROM articles
    WHERE id <> ?
    ORDER BY score DESC
    LIMIT 2
    """,
    [reading, 1],
)
for row in rows:
    print(round(row["score"], 3), row["title"])

Approximate search brings up related articles next; combine with full-text search for both words and meaning.

Alopex DB embeds in the device and brings up similar articles with an HNSW index. Start a read-later app with it; the quickstart is the way in.