Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions content/docs/kindred/blink/api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: API Reference
description: GraphQL endpoint, REST helpers, authentication, and rate limits for the Blink API.
---

The Blink API is served by a single Elysia process. It exposes a GraphQL endpoint, a small set of REST helpers, and standard health probes.

## Base URL

Hosted: `https://api.blink.omni.dev`

Self-hosted: whatever `API_BASE_URL` is set to in your Compose environment (default `http://localhost:4000`).

## Authentication

Authenticated requests carry a JWT bearer token in the `Authorization` header:

```
Authorization: Bearer <access_token>
```

Tokens are issued by Omni Identity (`identity.omni.dev`) via the OAuth 2.0 authorization code flow with PKCE. The frontend handles token acquisition and refresh; programmatic clients can mint tokens via the standard Identity API.

Public read paths (such as `/api/profile/by-username/:username/branding`) do not require authentication.

## GraphQL

Endpoint: `POST /graphql`

Schema introspection is **disabled in production**. To explore the schema, run `bun graphql:generate` in the API service to write the SDL to disk, or query a development instance where introspection is enabled.

Common operations:

- `query profileByUsername(username: String!)` to fetch a public profile and its tree
- `mutation createProfile`, `updateProfile`, `deleteProfile`
- `mutation createLink`, `updateLink`, `deleteLink`
- `mutation createTreeNode`, `updateTreeNode`, `deleteTreeNode`

All write mutations enforce Aether entitlements and Warden authorization checks.

## REST Helpers

| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| `GET` | `/health` | none | Liveness probe |
| `GET` | `/ready` | none | Readiness probe (checks DB) |
| `GET` | `/api/profile/by-username/:username/branding` | none | Returns `{ removeBranding: boolean }` for a public profile's organization |
| `GET` | `/api/entitlements/check?feature=&organizationId=` | bearer | Returns `{ allowed: boolean }` for the given feature key |
| `GET` | `/api/billing/prices` | none | Returns the live Stripe price catalog for Blink, including marketing features per tier |
| `POST` | `/api/billing/checkout` | bearer | Creates a Stripe Checkout session for the requested tier and returns the redirect URL |
| `POST` | `/api/billing/portal` | bearer | Creates a Stripe Customer Portal session for self-serve subscription management |

## Webhooks

The API receives webhooks from related services. Each path verifies its own HMAC signature.

| Path | Source | Purpose |
|------|--------|---------|
| `/webhooks/entitlements` | Aether | Invalidate cached entitlements on plan changes |
| `/webhooks/authz` | Vortex | Invalidate authorization tuples on org membership changes |
| `/webhooks/idp` | Gatekeeper | Sync user records on identity events |

## Rate Limiting

The API rate limits at **100 requests per minute per IP** (Elysia rate-limit plugin). Authenticated endpoints inherit the same limit. For higher throughput, contact us about Enterprise plans.

## CORS

The API only accepts cross-origin requests from origins listed in `CORS_ALLOWED_ORIGINS`. The hosted app (`https://blink.omni.dev`) is allowed by default in production.
57 changes: 57 additions & 0 deletions content/docs/kindred/blink/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Getting Started
description: Sign up for Blink, create your first profile, add links, and share your branded landing page.
---

This guide walks you from a brand-new account to a published profile in under five minutes. If you intend to run Blink on your own hardware, jump to [Self-Hosting](/kindred/blink/self-hosting).

## Create an account

1. Visit [blink.omni.dev](https://blink.omni.dev) and click **Sign In**.
2. You will be redirected to `identity.omni.dev` to authenticate with Google, GitHub, or any OIDC provider configured by your organization.
3. After signing in, you land on the Blink dashboard. A default organization is created for you on first sign-in.

## Create your first profile

A profile is the public page people see when they visit `blink.omni.dev/u/<username>`.

1. From the dashboard, click **New Profile**.
2. Pick a **username** (this becomes the URL slug), a **display name**, and optionally a short bio.
3. Upload an avatar if you have one handy.
4. Click **Save**. Your profile is now live at `https://blink.omni.dev/u/<username>`.

Free organizations are capped at ten links total across all profiles, plus 10 short links per month. Pro raises the link cap to 20 and 100 short links per month, Team removes both caps. See [Pricing](/kindred/blink/pricing) for the full breakdown.

## Add links

1. Open your profile and click **Add link**.
2. Choose the link **type** (regular link, short link, or section header).
3. Paste the destination URL, set a label, and optionally an icon.
4. Drag to reorder. Toggle visibility per link.

Short links count against your plan's monthly quota (`max_short_links_per_month`); regular links count against `max_links`. See [Pricing](/kindred/blink/pricing) for current limits.

## Customize the look

Pro and Team plans unlock custom themes:

- Open profile settings, click **Theme**, and pick a preset or define your own colors, fonts, and background.
- Team plans can also configure a **custom domain** (e.g., `links.yourbrand.com`) and remove the "Powered by Blink" footer.

Free profiles always show the Blink footer and use the default theme.

## Generate a QR code

1. From the dashboard or any link's detail page, click **Generate QR**.
2. Pick the size, error-correction level, and optional logo overlay.
3. Download the SVG or PNG. The QR resolves through Blink so you can swap the destination later without reprinting.

## Sharing and analytics

Every profile has a canonical URL (`blink.omni.dev/u/<username>`) and an OG image for previews in social apps. Analytics (per-link click counts, geo, referrers) ship on Pro and Team plans. The Free tier shows a "Coming soon" placeholder on the analytics page.

## Next steps

- [Pricing](/kindred/blink/pricing): plan comparison and what each entitlement unlocks
- [API Reference](/kindred/blink/api): GraphQL endpoint, REST helpers, webhooks
- [Self-Hosting](/kindred/blink/self-hosting): run Blink inside your own infrastructure
81 changes: 81 additions & 0 deletions content/docs/kindred/blink/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: 🖇️ Blink
description: One link, infinite facets. Centralize your online identity, share branded links, and manage QR codes from a single dashboard.
---

import { ProductOverview } from "@/components/docs";
import app from "@/lib/config/app.config";
import { FaIdCard, FaLink, FaQrcode } from "react-icons/fa";

<ProductOverview
tags={[
{
label: "Digital Identity",
icon: <FaIdCard />,
className: "bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-300",
},
{
label: "URL Shortener",
icon: <FaLink />,
className: "bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-300",
},
{
label: "QR Codes",
icon: <FaQrcode />,
className: "bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-300",
},
]}
links={[
{
href: "https://blink.omni.dev",
label: "Visit Website",
type: "website",
},
{
href: `${app.socials.github}/blink-stack`,
label: "Visit Repository",
type: "repository",
},
]}
alerts={[
{
title: "What is Blink?",
description:
"A unified link-in-bio platform with branded short links, custom themes, and per-organization workspaces. Apache 2.0 licensed and self-hostable.",
},
]}
/>

**Omni Blink** is a link-in-bio and short-link platform. Create a single shareable profile that points at every place you live online, generate branded QR codes, and shorten links with custom domains.

## Why Blink?

- **One profile, many facets**: a single public page that organizes every link you want to share
- **Branded short links**: per-organization short links with optional custom domains
- **Custom themes**: pick a preset or design your own to match your brand
- **Workspaces**: organizations and team seats backed by Omni's identity platform
- **Self-hostable**: ship it inside your own infrastructure with the official Compose stack

<Cards>
<Card title="Getting Started" href="/kindred/blink/getting-started">
Sign up, create your first profile, add links, and publish in under five minutes
</Card>
<Card title="Self-Hosting" href="/kindred/blink/self-hosting">
Run Blink inside your own infrastructure with Docker Compose
</Card>
<Card title="Pricing" href="/kindred/blink/pricing">
Free, Pro, and Team plans with entitlements enforced by Aether
</Card>
<Card title="API Reference" href="/kindred/blink/api">
GraphQL endpoint, REST helpers, authentication, and rate limits
</Card>
</Cards>

## Architecture

Blink runs as two services backed by PostgreSQL:

- `blink-api`: Elysia + PostGraphile GraphQL backend with Drizzle migrations
- `blink-app`: TanStack Start frontend (React 19) served as the public-facing application

The hosted version uses Omni's Identity (`identity.omni.dev`) for OAuth, Aether for billing entitlements, Warden for authorization, and the Fractal cluster for deployment. Self-hosters can wire any subset of these or run Blink standalone.
40 changes: 40 additions & 0 deletions content/docs/kindred/blink/pricing.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Pricing
description: Tier breakdown and entitlement enforcement for Blink.
---

Blink offers a free tier for individuals, paid tiers for power users and teams, and self-hosting for anyone who would rather run the stack themselves. Entitlements are enforced at the API by Aether and surfaced through the app.

## Plans

Pricing and tier definitions are owned by the [omni-api product catalog](https://github.com/omnidotdev/omni-api/blob/master/services/api/src/lib/db/catalog/planConfigs.ts) under the `blink` key. The values below mirror that source of truth.

| Plan | Monthly | Yearly | Links | Short links per month | Custom themes | Custom domain | Remove "Powered by Blink" | Analytics |
|------|---------|--------|-------|-----------------------|----------------|----------------|---------------------------|-----------|
| Free | $0 | $0 | 10 | 10 | No | No | No | No |
| Pro | $3 / mo | $27 / yr | 20 | 100 | Yes | No | No | Basic |
| Team | $8 / mo | $72 / yr | Unlimited | Unlimited | Yes | Yes | Yes | Advanced |

Yearly billing applies a 25% discount over the monthly rate. For enterprise terms, dedicated support, or on-prem deployments, [contact us](mailto:support@omni.dev).

## Entitlement Enforcement

Each entitlement is enforced server-side. The relevant feature keys map to checks in `services/blink-api/src/lib/entitlements`:

| Feature key | Where it's enforced |
|-------------|---------------------|
| `max_links` | Link create mutation |
| `max_short_links_per_month` | Short link create mutation |
| `custom_themes` | Profile update mutation when `customTheme` is set; also drives the analytics gate |
| `remove_branding` | Public profile renderer (hides the badge) |
| `custom_domain` | Custom domain settings tab |

When Aether is unreachable, the API falls back to free-tier defaults rather than failing requests. This keeps the app responsive during partial outages.

## Self-Hosting

Self-hosters do not pay anything to use Blink. The Apache 2.0 license covers commercial use, modification, and redistribution. Aether integration is optional. See [self-hosting](/kindred/blink/self-hosting) for setup.

## Billing

Hosted plans are billed monthly via Aether. Invoices, seat changes, and cancellations are handled in the Omni Console.
92 changes: 92 additions & 0 deletions content/docs/kindred/blink/self-hosting.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: Self-Hosting
description: Run Blink inside your own infrastructure with Docker Compose.
---

Blink ships an Apache 2.0 licensed reference deployment via Docker Compose. The metarepo at [`omnidotdev/blink-stack`](https://github.com/omnidotdev/blink-stack) provides everything needed to bring up the full stack.

## Prerequisites

- Docker (or any OCI runtime that speaks `compose` v2)
- A registered OAuth application if you want federated sign-in
- A reachable public hostname for the app and API if not running on localhost

## Services

The bundled `compose.yaml` defines three services. `api` and `app` are built from source on first `docker compose up`; subsequent runs use the cached images.

| Service | Image | Default Port |
|---------|-------|--------------|
| `db` | `postgres:18-alpine` | `5432` |
| `api` | `blink-api:latest` (built from `./services/blink-api`) | `4000` |
| `app` | `blink-app:latest` (built from `./services/blink-app`) | `3000` |

The API runs migrations on boot, then serves GraphQL at `/graphql` and REST helpers under `/api/`. The app is a TanStack Start server that proxies API calls to the API service.

## Required Environment Variables

Configure these in a `.env` next to `compose.yaml`. A starter file is included as `.env.local.template`.

| Variable | Purpose |
|----------|---------|
| `DB_PASSWORD` | PostgreSQL password used by the API to connect to `db` |
| `AUTH_SECRET` | Session signing secret. Generate with `openssl rand -hex 32` |

## Optional Environment Variables

| Variable | Purpose |
|----------|---------|
| `APP_BASE_URL` | Public URL of the app, used for CORS and OAuth callbacks. Defaults to `http://localhost:3000` |
| `API_BASE_URL` | URL the app uses to reach the API. Defaults to `http://api:4000` |
| `DB_NAME`, `DB_USER`, `DB_PORT` | Postgres overrides |
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | Enable Google OAuth |
| `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | Enable GitHub OAuth |
| `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET` | Generic OIDC provider |

If no OAuth provider is configured, sign-in features are disabled. The app boots successfully with only `DB_PASSWORD` and `AUTH_SECRET` set.

## Running

```sh
git clone https://github.com/omnidotdev/blink-stack.git
cd blink-stack
cp services.yaml.template services.yaml
cp .env.local.template .env
# edit .env to set DB_PASSWORD and AUTH_SECRET
docker compose up -d --build
```

The `--build` flag is only needed the first time; subsequent `docker compose up` runs reuse the cached images.

The API is reachable at `http://localhost:4000`, the app at `http://localhost:3000`.

## Diagnostics

Verify each service is healthy:

```sh
curl http://localhost:4000/health
curl http://localhost:4000/ready
curl http://localhost:3000
```

`GET /ready` returns `503` if the database is not reachable. Logs are available via `docker compose logs -f api app`.

## Volumes

`db_data` is the only declared volume and persists Postgres state. Back it up before any image upgrade.

## Optional Integrations

Blink can integrate with other Omni services if those URLs are configured:

- `BILLING_BASE_URL` and `BILLING_SERVICE_API_KEY`: enable Aether-backed entitlements (otherwise free-tier defaults apply)
- `AUTHZ_API_URL`: enable Warden authorization checks (otherwise allow-by-default within the app)
- `VORTEX_API_URL`: enable cross-product event delivery
- `MEILISEARCH_URL` and `MEILISEARCH_MASTER_KEY`: enable search indexing

If these are unset, the API logs a warning at boot and continues with the corresponding feature disabled.

## License

Blink is licensed under Apache 2.0. See `LICENSE.md` in the metarepo for details.
Loading
Loading