OpenLinks is a personal, free, open source, version-controlled static website generator for social links.
This project is developer-first, but that does not mean raw JSON should be your default CRUD surface. For most maintainers, the recommended path is:
- Use the repo's AI workflows/skills and checked-in automation docs for repo-native CRUD.
- Use the Studio webapp when the browser-based self-serve editor fits your workflow.
- Drop to direct JSON edits only when you need lower-level control or a manual fallback.
- Governance doc: docs/logo-governance.md
- Global primary logo:
public/branding/openlinks-logo/openlinks-logo.svg - Explicit V2 alias:
public/branding/openlinks-logo/openlinks-logo-v2.svg - Archived non-winning V2 marks:
public/branding/openlinks-logo/v2/archive/ - Runtime browser icon set (main app):
public/ - Runtime browser icon set (Studio):
packages/studio-web/public/ - Regenerate runtime brand assets:
bun run branding:assets
OpenLinks generates one canonical site badge asset during dev and build at:
<deployed-origin>/badges/openlinks.svg
Default behavior:
- label is fixed to
OpenLinks - message defaults to
profile.name - optional override lives at
site.sharing.badge.message - set
site.sharing.badge.enabledtofalseto stop publishing the badge
Example override in data/site.json:
{
"sharing": {
"badge": {
"message": "My OpenLinks"
}
}
}Recommended Markdown embed once your site is deployed:
[](https://<your-domain>/)GitHub Pages fork example:
[](https://<owner>.github.io/<repo>/)- Static SolidJS site with minimal runtime complexity.
- Version-controlled content in
data/*.json. - Schema + policy validation with actionable remediation output.
- Rich and simple card support with build-time enrichment.
- Payments and tips links with multi-rail support, styled QR codes, and fullscreen scan mode.
- Build-time profile-avatar materialization with local fallback behavior.
- Build-time rich/SEO image materialization with local-only runtime behavior.
- GitHub Actions CI + AWS canonical deploy + GitHub Pages mirror pipeline already wired.
- Theme and layout controls designed for forking and customization.
- Data-driven typography overrides via
data/site.json(ui.typography).
- Developers who are comfortable editing JSON and markdown.
- Maintainers using AI agents to automate content updates.
- User auth/account system.
- CMS or WYSIWYG editor.
- Traffic analytics or pageview dashboards.
- Plugin marketplace.
For full walkthrough and troubleshooting, see Quickstart.
- Preferred for repo-native maintenance: use the repo's AI workflows/skills through OpenClaw Update/CRUD Contract, OpenClaw Bootstrap Contract, AI-Guided Customization Wizard, Linktree Bootstrap Extractor,
skills/cache-rich-link-assets/SKILL.mdwhen rich-link image assets need to be committed, andskills/openlinks-fork-identity-presence/SKILL.mdwhen you want other websites, repos, docs, or services to point back to your deployed OpenLinks fork. - Preferred for browser-based CRUD: use OpenLinks Studio when the self-serve onboarding/editor already covers your workflow.
- Manual fallback: edit
data/*.jsondirectly only when you intentionally want the lower-level path or need to work outside the currently supported AI/Studio flows.
The repository currently ships these repo-local skill entrypoints under skills/:
skills/openlinks-fork-identity-presence/SKILL.md: help other websites, apps, repos, docs, and service profiles point back to your deployed OpenLinks fork using its canonical URL and brand assets.skills/cache-rich-link-assets/SKILL.md: persist rich-card images and related metadata into the committed cache after link changes.skills/create-new-rich-content-extractor/SKILL.md: public-first workflow for deciding and implementing new rich metadata support when existing enrichment is insufficient.
Paste this one-liner into OpenClaw, Claude, or Codex (the prompts are mostly compatible with any coding agent):
Follow https://raw.githubusercontent.com/pRizz/open-links/main/docs/openclaw-bootstrap.md exactly for this repository. Execute Required Execution Policy, End-to-End OpenClaw Sequence, Automation and Identity Confirmation Rule, Social Discovery and Inference Contract, Deployment Verification Contract, Structured URL Reporting Schema, README Deploy URL Marker-Block Contract, and Final Output Contract exactly as written. If an existing setup is detected, ask the single route-confirmation and switch to https://raw.githubusercontent.com/pRizz/open-links/main/docs/openclaw-update-crud.md when selected.
Use this path when this is the first setup for a new fork or local clone.
Paste this one-liner into OpenClaw, Claude, or Codex:
Follow https://raw.githubusercontent.com/pRizz/open-links/main/docs/openclaw-update-crud.md exactly for this repository. Execute Required Startup Handshake (including conditional customization-audit selectors), Defaults, Customization Audit Path (Optional), Repository Resolution, Dirty Local Repository Handling, Interaction Modes, Identity and Discovery Policy, Update/CRUD Execution Sequence, Final Output Contract, and Required reason codes exactly as written. When customization_path=customization-audit, use https://raw.githubusercontent.com/pRizz/open-links/main/docs/customization-catalog.md as the checklist source.
Use this path for day-2 maintenance when the user likely already has a fork and/or local clone.
Use this approach:
- Fork this repository.
git clone <your-repo-url>
cd open-links
bun installIf you have a Linktree and want a bootstrap seed for profile/avatar/social/content links before editing data/*.json, run:
bun run bootstrap:linktree -- --url https://linktr.ee/<handle>If your links use authenticated extractors (links[].enrichment.authenticatedExtractor), run guided cache setup before first dev/build:
bun run setup:rich-authIf you use Medium, X, or Primal rich links and want the optional public audience metrics cached locally, run:
bun run public:rich:sync -- --only-link medium
bun run public:rich:sync -- --only-link x
bun run public:rich:sync -- --only-link primalIf you want to refresh the public follower-history artifacts locally before the nightly automation does it on main, run:
bun run followers:history:syncPreferred path:
- use the repo AI workflows/skills and the docs above for routine CRUD
- use OpenLinks Studio when you want the browser-based self-serve path
Manual fallback:
-
edit these files directly when you need the lower-level path
-
data/profile.json- identity and profile details. -
data/links.json- simple/rich/payment links, groups, ordering. -
data/site.json- theme, UI, quality, and deployment-related config.
Linktree-assisted bootstrap:
- use
bun run bootstrap:linktree -- --url https://linktr.ee/<handle>to generate reviewable profile/link candidates before editingdata/profile.jsonanddata/links.json
Starter presets:
data/examples/minimal/data/examples/grouped/data/examples/invalid/(intentional failures for testing)
bun run validate:data
bun run devbun run build
bun run previewOpenClaw should update only the rows between the exact marker lines below:
- rewrite only marker-bounded rows,
- commit only if normalized URL/status values changed.
OPENCLAW_DEPLOY_URLS_START
| target | status | primary_url | additional_urls | evidence |
|---|---|---|---|---|
| aws | active | https://openlinks.us/ | none | Deploy Production -> Deploy AWS Canonical Site |
| github-pages | active | https://prizz.github.io/open-links/ | canonical=https://openlinks.us/ | Deploy Production -> Deploy GitHub Pages Mirror |
| OPENCLAW_DEPLOY_URLS_END |
If you want an AI agent workflow with explicit checkpoints and manual opt-outs, use the AI-Guided Customization Wizard. For automation-first execution paths, use OpenClaw Bootstrap Contract for first-time setup and OpenClaw Update/CRUD Contract for day-2 changes.
Recommended flow:
- Start with Quickstart.
- Prefer OpenClaw Update/CRUD Contract or AI-Guided Customization Wizard for routine repo-native CRUD.
- Use OpenLinks Studio when you want the browser-based self-serve path.
- Use Data Model and Customization Catalog as the contract/reference layer.
- Use Social Card Verification Guide after changing profile-card metadata, follower history, analytics, or share behavior.
- Push to
main. - In GitHub repository settings, set Pages source to GitHub Actions.
- For the upstream
openlinks.usdeploy, runbun run deploy:setupfirst, then rerun with--applyonce the check-mode summary is clean. - Wait for:
.github/workflows/ci.ymlto succeed..github/workflows/deploy-pages.yml(Deploy Production) to deploy.
- Open
https://openlinks.us/and the Pages mirror URL from the deployment job summary.
Local parity commands:
bun run ci:required
bun run ci:strictThen use:
- Deployment Operations Guide for full troubleshooting and diagnostics flow.
- OpenClaw Bootstrap Contract for deployment URL reporting and README marker-block updates.
- OpenClaw Update/CRUD Contract for existing repo update sessions and interaction-mode behavior.
- Linktree Bootstrap Extractor for Linktree-first bootstrap of profile/link candidates.
- Adapter Contract Guide for future non-GitHub host planning.
This repository now includes a multi-service self-serve control plane for non-technical onboarding and browser-based CRUD edits:
packages/studio-web- marketing + onboarding + editor (Solid + Tailwind + shadcn-solid style components)packages/studio-api- GitHub auth/app workflows, fork provisioning, content validation/commit, deploy status, sync endpointspackages/studio-worker- scheduled sync trigger workerpackages/studio-shared- shared contracts
See docs/studio-self-serve.md for local setup, Railway deployment, env variables, and GitHub App setup. Track implementation phases in docs/studio-phase-checklist.md.
Studio workspace tooling is Bun-first:
bun installbun run studio:typecheckbun run studio:lintbun run studio:format
High-signal deployment checks:
required-checksjob in.github/workflows/ci.ymlis green.Deploy AWS Canonical Sitejob in.github/workflows/deploy-pages.ymlis green when AWS deploy is enabled.Deploy GitHub Pages Mirrorjob in.github/workflows/deploy-pages.ymlis green or intentionally skipped as a no-op.Verify Production Deploymentis green when AWS deploy is enabled.- If deploy fails, review workflow summaries and diagnostics artifacts.
bun run avatar:sync- fetch profile avatar intopublic/cache/profile-avatar/, write the committed manifestdata/cache/profile-avatar.json, and refresh the gitignored runtime overlaydata/cache/profile-avatar.runtime.json.bun run enrich:rich- run non-strict rich metadata enrichment (diagnostic/manual mode) with known-blocker + authenticated-cache policy enforcement; routine runs leavedata/cache/rich-public-cache.jsonunchanged and only update the local runtime overlay when needed.bun run enrich:rich:write-cache- run non-strict rich enrichment and explicitly persist refreshed public metadata intodata/cache/rich-public-cache.json.bun run enrich:rich:strict- run policy-enforced rich metadata enrichment (blocking mode) with known-blocker + authenticated-cache policy enforcement; routine runs leavedata/cache/rich-public-cache.jsonunchanged and only update the local runtime overlay when needed.bun run enrich:rich:strict:write-cache- run policy-enforced rich enrichment and explicitly persist refreshed public metadata intodata/cache/rich-public-cache.json.bun run public:rich:sync- refresh public browser-derived Medium/X/Primal profile audience metrics into the committed stable cache atdata/cache/rich-public-cache.jsonand the local runtime overlay atdata/cache/rich-public-cache.runtime.json(non-auth, operator-invoked).bun run followers:history:sync- append the current follower/subscriber snapshots into the public CSV history files underpublic/history/followers/and refreshpublic/history/followers/index.json.bun run setup:rich-auth- first-run authenticated cache setup (captures only missing/invalid authenticated cache entries).bun run auth:rich:sync- guided authenticated rich-cache capture (updatesdata/cache/rich-authenticated-cache.json+public/cache/rich-authenticated/*).bun run auth:rich:clear- clear authenticated cache entries and unreferenced local assets (selector-driven; supports--dry-run).bun run auth:extractor:new -- --id <id> --domains <csv> --summary "<summary>"- scaffold a new authenticated extractor plugin + policy + registry wiring.bun run linkedin:debug:bootstrap- LinkedIn debug bootstrap (agent-browser checks + browser binary install check).bun run linkedin:debug:login- LinkedIn debug login watcher (autonomous auth-state polling; multi-factor authentication optional).bun run linkedin:debug:validate- LinkedIn authenticated metadata debug validator.bun run linkedin:debug:validate:cookie-bridge- LinkedIn debug validator with cookie-bridge HTTP diagnostic.bun run images:sync- fetch rich-card/SEO remote images into the committed cache atpublic/cache/content-images/, write the stable manifestdata/cache/content-images.json, and refresh the gitignored runtime overlaydata/cache/content-images.runtime.json.- Cache-backed fetches are governed by the committed per-domain registry
data/policy/remote-cache-policy.json. New remote hosts must be added there in the same change batch as link/extractor updates. bun run dev- start local dev server (predev runs strict enrichment in read-only public-cache mode and fails on blocking enrichment policy issues).bun run validate:data- schema + policy checks (standard mode).bun run validate:data:strict- fails on warnings and errors.bun run validate:data:json- machine-readable validation output.bun run build- avatar sync + strict enrichment + content-image sync + validation + production build. The strict enrichment pre-step updates only the local runtime overlay unless you intentionally ran a*:write-cachecommand beforehand;images:syncrefreshes committed content-image cache artifacts when image bytes change.bun run build:strict- avatar sync + strict enrichment + content-image sync + strict validation + build. The strict enrichment pre-step updates only the local runtime overlay unless you intentionally ran a*:write-cachecommand beforehand;images:syncrefreshes committed content-image cache artifacts when image bytes change.bun run preview- serve built output.bun run typecheck- TypeScript checks.
- Workflow:
.github/workflows/nightly-follower-history.yml - Public artifacts:
public/history/followers/*.csvpublic/history/followers/index.json
- Local parity:
bun run enrich:rich:strict:write-cachebun run public:rich:syncbun run followers:history:syncbun run build
- The workflow commits directly to
mainand deploys Pages in the same run. This avoids depending on downstream workflow fan-out from a bot-authored push.
Canonical paths:
data/cache/rich-authenticated-cache.jsonpublic/cache/rich-authenticated/output/playwright/auth-rich-sync/
Setup/refresh flow:
- First-run idempotent setup (only missing/invalid cache entries):
bun run setup:rich-auth - Targeted refresh:
bun run auth:rich:sync -- --only-link <link-id> - Forced refresh (even when cache is already valid):
bun run auth:rich:sync -- --only-link <link-id> --force
Clear flow:
- Dry run clear for one link:
bun run auth:rich:clear -- --only-link <link-id> --dry-run - Apply clear for one link:
bun run auth:rich:clear -- --only-link <link-id> - Apply clear for all authenticated cache entries:
bun run auth:rich:clear -- --all - Recapture after clear:
bun run setup:rich-auth(orbun run auth:rich:sync -- --only-link <link-id>)
bun run quality:check- standard quality gate.bun run quality:strict- strict quality gate.bun run quality:json- standard quality JSON report.bun run quality:strict:json- strict quality JSON report.
bun run ci:required- required CI checks.bun run ci:strict- strict CI signal checks.
Allowed URL schemes:
httphttpsmailtotel
Payment-enabled links and payment rails additionally support:
bitcoinlightningethereumsolana
Use custom for extension metadata:
- top-level
custominprofile,links, andsite - per-link
customin each link object
Unknown top-level keys are allowed but warned. custom keys that collide with core keys fail validation.
For full data model details and examples, see Data Model.
- Re-run
bun run validate:dataand inspect path-specific remediation lines. - Check URL schemes and required fields.
- Move extension fields into
customand avoid reserved-key collisions.
- Re-run with
bun run buildand inspect first failing command output. - If strict mode fails, compare
bun run validate:datavsbun run validate:data:strict. - Re-run blocking enrichment diagnostics:
bun run enrich:rich:strict. - Check canonical blocker registry:
data/policy/rich-enrichment-blockers.json. - Check authenticated extractor policy:
data/policy/rich-authenticated-extractors.json. - Check authenticated cache manifest:
data/cache/rich-authenticated-cache.json. - Review known blocked rich-metadata domains and timestamped attempt history:
docs/rich-metadata-fetch-blockers.md. - Review authenticated extractor architecture/workflow:
docs/authenticated-rich-extractors.md. - Check
site.ui.richCards.enrichmentpolicy (failureMode,failOn,allowManualMetadataFallback) indata/site.json. - If rich-card images look clipped, set
site.ui.richCards.imageFit=contain(or per-link override withlinks[].metadata.imageFit). - If a blocked domain must be tested anyway, set explicit override on that link:
links[].enrichment.allowKnownBlocker=true. - If
authenticated_cache_missingis reported, runbun run setup:rich-auth(orbun run auth:rich:sync -- --only-link <link-id>) and commit cache manifest/assets. - To reset stale/bad authenticated cache data, clear entries first with
bun run auth:rich:clear -- --only-link <link-id>(or--all), then recapture withbun run setup:rich-auth. - If
metadata_missingis blocking, add at least one manual field underlinks[].metadata(title,description, orimage) or remediate remote OG/Twitter metadata. - If a manual or enriched rich-link image changed, run
bun run images:syncand commitdata/cache/content-images.jsonpluspublic/cache/content-images/*when they update. - Temporary emergency bypass (local only):
OPENLINKS_RICH_ENRICHMENT_BYPASS=1 bun run build. - Force-refresh avatar cache when needed:
bun run avatar:sync -- --force(or setOPENLINKS_AVATAR_FORCE=1). - Force-refresh rich/SEO image cache when needed:
bun run images:sync -- --force(or setOPENLINKS_IMAGES_FORCE=1).
- Confirm CI passed on
main. - Confirm Pages source is GitHub Actions.
- Check deploy workflow summary for remediation notes.
- Verify base-path settings if publishing from a project page.
- Quickstart
- OpenClaw Bootstrap Contract
- OpenClaw Update/CRUD Contract
- Agent Triage Contract
- Customization Catalog
- Data Model
- Rich Metadata Fetch Blockers
- Rich Enrichment Blockers Registry
- Authenticated Rich Extractors
- Create New Rich Content Extractor
- LinkedIn Authenticated Metadata Debug Runbook
- Repo Skill: OpenLinks Fork Identity Presence
- Repo Skill: Cache Rich Link Assets
- Repo Skill: Create New Rich Content Extractor
- AI-Guided Customization Wizard
- Theming and Layout Extensibility
- Deployment Operations Guide
- Adapter Contract Guide
data/- source content JSON and generated artifacts.schema/- JSON schemas.scripts/- validation, enrichment, and quality runners.src/- SolidJS app..github/workflows/- CI and deploy automation..planning/- project planning and phase artifacts.
If extractor workflows helped you build new or improved extractors, kindly consider opening a pull request against https://github.com/pRizz/open-links so everyone can benefit. Feedback on extractor workflows and docs is appreciated.
MIT (see LICENSE).
