A server-side link shortening service powered by Linkat
1# AT Protocol Link Shortener
2
3A **server-side** link shortening service powered by your [Linkat](https://linkat.blue) board. No database required - all links are fetched directly from AT Protocol!
4
5## ✨ Features
6
7- **Zero Configuration Database**: Uses your existing Linkat board as the data source
8- ⚡ **Hash-Based Shortcodes**: Automatic 6-character codes generated from URLs (e.g., `/a3k9zx`)
9- 🚀 **Server-Side Only**: Pure API-based, no client UI needed
10- 🎯 **Smart Redirects**: Instant HTTP 301 redirects to your target URLs
11- 🔍 **Automatic PDS Discovery**: Resolves your PDS endpoint via Slingshot
12- ⚡ **Built-in Cache**: 5-minute cache for optimal performance
13- 🎨 **Tailwind CSS 4**: Modern styling with the latest Tailwind version
14
15## 🚀 Quick Start
16
17### 1. Clone and Install
18
19```bash
20git clone git@github.com:ewanc26/atproto-shortlink # or git@tangled.sh:ewancroft.uk/atproto-shortlink
21cd atproto-shortlink
22npm install
23```
24
25### 2. Configure Your DID
26
27Create a `.env` file:
28
29```bash
30cp .env.example .env
31```
32
33Edit `.env` and add your AT Protocol DID:
34
35```ini
36# Find your DID at https://pdsls.dev/ by entering your handle
37ATPROTO_DID=did:plc:your-did-here
38```
39
40**How to find your DID:**
41
421. Visit [PDSls](https://pdsls.dev/)
432. Enter your AT Protocol handle (e.g., `yourname.bsky.social`)
443. Copy the `did:plc:...` identifier
45
46### 3. Set Up Your Linkat Board
47
48If you don't have a Linkat board yet:
49
501. Visit [https://linkat.blue](https://linkat.blue)
512. Create a board with your links
523. Add your links with titles and emojis
53
54The shortener will automatically generate unique codes for each URL!
55
56### 4. Test Your Configuration (Optional)
57
58Run the configuration test to verify everything is set up correctly:
59
60```bash
61npm run test:config
62```
63
64This will:
65
66- ✅ Check if `.env` exists and is configured
67- ✅ Validate your DID format
68- ✅ Test PDS connectivity
69- ✅ Verify your Linkat board is accessible
70- ✅ Show a preview of your first few links
71
72### 5. Run the Server
73
74```bash
75npm run dev
76```
77
78Visit `http://localhost:5173` to see your service running!
79
80## 📖 Usage
81
82Once running, your short links work like this:
83
84```bash
85# Redirect to your configured URLs
86http://localhost:5173/a3k9zx → Redirects to your GitHub
87http://localhost:5173/b7m2wp → Redirects to your blog
88http://localhost:5173/c4n8qz → Redirects to your portfolio
89
90# View service info
91http://localhost:5173/ → Shows API information and available links
92
93# Get JSON list of links
94http://localhost:5173/api/links → Returns all short links as JSON
95```
96
97## 🔧 API Endpoints
98
99| Endpoint | Method | Description | Response |
100| ------------- | ------ | ------------------------------- | ------------ |
101| `/` | GET | Service status and link listing | HTML |
102| `/:shortcode` | GET | Redirect to full URL | 301 Redirect |
103| `/api/links` | GET | List all available short links | JSON |
104
105### Example API Response
106
107```json
108{
109 "success": true,
110 "count": 3,
111 "links": [
112 {
113 "shortcode": "a3k9zx",
114 "url": "https://github.com/yourname",
115 "title": "My GitHub Profile",
116 "emoji": "💻",
117 "shortUrl": "/a3k9zx"
118 },
119 {
120 "shortcode": "b7m2wp",
121 "url": "https://yourblog.com",
122 "title": "Personal Blog",
123 "emoji": "📝",
124 "shortUrl": "/b7m2wp"
125 }
126 ]
127}
128```
129
130## 📝 How Shortcodes Work
131
132Shortcodes are automatically generated as 6-character base62 hashes from your URLs. Each URL will always produce the same shortcode, ensuring consistency.
133
134- **Base62 encoding**: Uses 0-9, a-z, A-Z (62 characters)
135- **Collision-resistant**: 62^6 = ~56 billion possible combinations
136- **Deterministic**: Same URL = same shortcode every time
137- **URL-safe**: No special characters needed
138
139## 🌐 Deployment
140
141### Build for Production
142
143```bash
144npm run build
145npm run preview # Test the production build locally
146```
147
148### Deploy to Platforms
149
150This project uses `@sveltejs/adapter-auto` which works with:
151
152- **Vercel**: Push to GitHub and connect your repo
153- **Netlify**: Push to GitHub and connect your repo
154- **Cloudflare Pages**: Push to GitHub and connect your repo
155- **Node.js**: Use `adapter-node` for standalone Node servers
156
157For specific platforms, see [SvelteKit adapters](https://kit.svelte.dev/docs/adapters).
158
159### Environment Variables for Deployment
160
161Make sure to set `ATPROTO_DID` in your deployment platform's environment variables!
162
163## ⚙️ Configuration
164
165| Variable | Required | Description | Example |
166| ------------- | -------- | -------------------- | ------------------- |
167| `ATPROTO_DID` | ✅ Yes | Your AT Protocol DID | `did:plc:abc123xyz` |
168
169## 🏗️ How It Works
170
1711. **You maintain your links** in [Linkat](https://linkat.blue) (stored in `blue.linkat.board` collection)
1722. **Service fetches on-demand** from your AT Protocol PDS via Slingshot resolution
1733. **URLs are shortened** using deterministic base62 hash encoding
1744. **Accessing a short link** (e.g., `/a3k9zx`) triggers an instant 301 redirect
175
176```mermaid
177graph LR
178 A[User visits /a3k9zx] --> B[Service fetches Linkat data]
179 B --> C[Looks up shortcode in links]
180 C --> D[301 Redirect to target URL]
181```
182
183## 🔒 Security
184
185- ✅ All Linkat data is public by design
186- ✅ No authentication required
187- ✅ Read-only access to AT Protocol data
188- ✅ No data storage (fetches on-demand with cache)
189- ✅ 5-minute cache to prevent abuse
190
191## 🛠️ Development
192
193```bash
194# Install dependencies
195npm install
196
197# Start dev server
198npm run dev
199
200# Type check
201npm run check
202
203# Format code
204npm run format
205
206# Check formatting
207npm run lint
208```
209
210## 🎨 Styling with Tailwind CSS 4
211
212This project uses **Tailwind CSS 4** with the new Vite plugin.
213
214Key features:
215
216- ✅ Native CSS imports with `@import 'tailwindcss'`
217- ✅ Faster builds with the Vite plugin
218- ✅ Automatic dark mode support
219- ✅ No config file needed for basic usage
220
221## 📦 Tech Stack
222
223- **Framework**: [SvelteKit 2](https://kit.svelte.dev/)
224- **Styling**: [Tailwind CSS 4](https://tailwindcss.com/)
225- **Runtime**: Server-side only (no client JavaScript required)
226- **Data Source**: AT Protocol (`blue.linkat.board` collection)
227- **PDS Resolution**: [Slingshot](https://slingshot.microcosm.blue) by Microcosm
228- **Redirects**: HTTP 301 (permanent)
229- **Shortcode Format**: Base62 hash encoding
230
231## 🔧 Troubleshooting
232
233Having issues? Check the [Troubleshooting Guide](./TROUBLESHOOTING.md) for common problems and solutions.
234
235Quick checks:
236
2371. Run `npm run test:config` to verify your setup
2382. Make sure Node.js 18+ is installed: `node --version`
2393. Check your DID at [pdsls.dev](https://pdsls.dev/)
2404. Verify your Linkat board at [linkat.blue](https://linkat.blue)
241
242## 🤝 Contributing
243
244Contributions are welcome! Please feel free to submit a Pull Request.
245
246## 📄 Licence
247
248AGPLv3 Licence - See [LICENCE](./LICENCE) file for details
249
250## Links
251
252- [Linkat](https://linkat.blue) - The link board service
253- [AT Protocol](https://atproto.com) - The underlying protocol
254- [SvelteKit](https://kit.svelte.dev) - The web framework
255- [Tailwind CSS](https://tailwindcss.com) - CSS framework
256- [PDSls](https://pdsls.dev/) - Find your DID
257- [Slingshot](https://slingshot.microcosm.blue) - Identity resolver
258
259---
260
261Made with ❤️ using AT Protocol and Linkat