The Appview for the kipclip.com atproto bookmarking service
TypeScript 90%
Shell 8%
HTML <1%
<1%
Go <1%
CSS <1%
JavaScript <1%

README.md

kipclip #

Test codecov

Ko-fi

Find it, Kip it. Save and organize bookmarks using the AT Protocol community bookmark lexicon.

Features #

  • AT Protocol OAuth authentication
  • Save bookmarks to your personal data server (PDS)
  • Automatic URL enrichment (title extraction)
  • View and manage your bookmarks
  • Reading List: filter bookmarks by a configurable tag (default: "toread")
  • Uses community.lexicon.bookmarks.bookmark schema

Architecture #

  • Frontend: React 19 + TypeScript + Tailwind CSS
  • Backend: Fresh 2.x on a Hetzner box (kipclip.com). Pull-based releases via signed v* git tags
  • AppView mirror: every tracked user's bookmark / tag / annotation / preference records are mirrored from the user's PDS into a local libSQL database on the box, kept fresh by TAP webhooks. Reads serve from the mirror; writes always go to the user's PDS via AT Protocol
  • Database: local SQLite (DATABASE_URL) for all reads, sessions, and settings
  • Bookmark storage (source of truth): user's PDS (not the AppView)
  • Static assets: Bunny CDN (cdn.kipclip.com)
  • Edge: Caddy on the box (TLS, security headers, CSP+SRI)

Project Structure #

kipclip-appview/
├── main.ts              # Fresh app entry point (all routes)
├── dev.ts               # Development server
├── lib/                 # Backend utilities
│   ├── db.ts            # Database client
│   ├── oauth-config.ts  # OAuth configuration
│   ├── enrichment.ts    # URL metadata extraction
│   └── ...
├── frontend/
│   ├── components/      # React components
│   ├── index.html       # Entry HTML
│   ├── index.tsx        # React entry
│   └── style.css        # Custom styles
├── shared/
│   ├── types.ts         # Shared TypeScript types
│   └── utils.ts         # Shared utilities
└── tests/               # Test files

Setup #

Prerequisites #

  • Deno installed
  • Deno and a writable local filesystem (for SQLite)

Environment Variables #

COOKIE_SECRET=your-random-secret-string-at-least-32-chars  # Required
DATABASE_URL=file:/var/lib/kipclip/kipclip.db  # Optional, defaults to file:.local/kipclip.db
BASE_URL=https://kipclip.com  # Optional, derived from request if not set

The COOKIE_SECRET is required for encrypting OAuth session cookies.

Local Development #

# Run local dev server
deno task dev

# Type check
deno task check

# Run quality checks (format, lint)
deno task quality

# Run tests
deno task test

Mascot #

The kipclip mascot is Kip, a friendly chicken. Mascot images are hosted on Bunny CDN at cdn.kipclip.com/images/.

OAuth Flow #

kipclip uses @tijs/atproto-oauth for AT Protocol authentication.

  1. User enters their AT Protocol handle
  2. App redirects to /login?handle=user.bsky.social
  3. OAuth package handles authentication with user's PDS
  4. Session stored in local SQLite (14 days)
  5. User can now view/add bookmarks

For implementation details, see the package documentation.

Dependencies #

API Endpoints #

Bookmarks #

  • GET /api/bookmarks - List user's bookmarks from PDS
  • POST /api/bookmarks - Add new bookmark with URL enrichment
  • PATCH /api/bookmarks/:rkey - Update bookmark (tags, title, etc.)
  • DELETE /api/bookmarks/:rkey - Delete a bookmark

Tags #

  • GET /api/tags - List user's tags
  • POST /api/tags - Create a new tag
  • PUT /api/tags/:rkey - Update tag (renames across all bookmarks)
  • DELETE /api/tags/:rkey - Delete tag (removes from all bookmarks)

Auth #

  • GET /api/auth/session - Check current session
  • POST /api/auth/logout - Logout
  • /login - OAuth login flow
  • /oauth/callback - OAuth callback

Settings #

  • GET /api/settings - Get user settings
  • PATCH /api/settings - Update user settings

Sharing #

  • GET /api/share/:did/:encodedTags - Get shared bookmarks (public)
  • GET /share/:did/:encodedTags/rss - RSS feed for shared bookmarks

Bookmark Schema #

Bookmarks are stored using the community lexicon:

{
  subject: string;      // URL being bookmarked
  createdAt: string;    // ISO 8601 datetime
  tags?: string[];      // Optional tags
}

The app enriches bookmarks with page titles by fetching and parsing HTML metadata.

Development Guidelines #

  • Keep code files under 500 lines
  • Write testable code with dependency injection
  • Tests are in tests/ directory
  • Use TypeScript for all code

License #

MIT