{"id":48255792,"url":"https://github.com/kristoferlund/promptathon-showcase","last_synced_at":"2026-04-04T21:12:08.623Z","repository":{"id":338891030,"uuid":"1156300708","full_name":"kristoferlund/promptathon-showcase","owner":"kristoferlund","description":"This is the January promptahon showcase! The competition saw more than 800 registered participants and more than 200 submitted apps.","archived":false,"fork":false,"pushed_at":"2026-03-06T11:24:25.000Z","size":11599,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-06T15:39:52.751Z","etag":null,"topics":["internet-computer","rust","sqlite"],"latest_commit_sha":null,"homepage":"https://january-promptathon.xyz","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kristoferlund.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-12T13:50:38.000Z","updated_at":"2026-03-06T11:24:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kristoferlund/promptathon-showcase","commit_stats":null,"previous_names":["kristoferlund/promptathon-showcase"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kristoferlund/promptathon-showcase","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fpromptathon-showcase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fpromptathon-showcase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fpromptathon-showcase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fpromptathon-showcase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kristoferlund","download_url":"https://codeload.github.com/kristoferlund/promptathon-showcase/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fpromptathon-showcase/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31414408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["internet-computer","rust","sqlite"],"created_at":"2026-04-04T21:12:08.018Z","updated_at":"2026-04-04T21:12:08.618Z","avatar_url":"https://github.com/kristoferlund.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Promptathon Showcase\n\n![Promptathon Showcase](media/screendump.png)\n\nThe January competition saw more than 800 registered participants and 183 submitted apps. This is the showcase app, a fully on-chain app gallery running on the Internet Computer. Every asset, every database query, every server-rendered page, and every dynamically generated OG image is served directly from a single canister with certified HTTP responses.\n\n## What Makes This Interesting\n\n### Certified HTTP Asset Serving with `ic-asset-router`\n\nAll HTTP responses — static and dynamic — are served with IC response certification. Crawlers and social platforms receive cryptographically verifiable responses, the same guarantee that applies to any call on the Internet Computer.\n\nThe project uses [`ic-asset-router`](https://github.com/kristoferlund/ic-asset-router), a file-based routing library for IC canisters inspired by Next.js conventions. Route handlers are plain Rust functions organized by directory structure:\n\n```\nserver/src/routes/\n├── index.rs              → GET /\n├── not_found.rs          → 404 handler\n└── app/\n    └── _id/\n        ├── index.rs      → GET /app/:id\n        └── og.png.rs     → GET /app/:id/og.png\n```\n\nRoute modules, parameter extraction, and handler registration are all generated at build time — no manual wiring needed.\n\n### Dynamic OG Image Generation\n\nEach app gets a unique Open Graph image rendered on-chain. The pipeline:\n\n1. An SVG template is rendered with [MiniJinja](https://github.com/mitsuhiko/minijinja), injecting the app name and title\n2. Two bundled fonts (Sohne Breit Halbfett for headings, Sohne Leicht for subtitles) are loaded into a `fontdb` database\n3. [resvg](https://github.com/nickel-org/resvg) rasterizes the SVG to a 1200x630 PNG — standard OG image dimensions\n4. The result is certified and cached with a 30-day `Cache-Control` header\n\nNo external services. The canister generates, certifies, and serves the image.\n\n### Server-Side Rendered Meta Tags\n\nThe Vite-built `index.html` contains MiniJinja template placeholders in the `\u003chead\u003e`. Each route handler renders the template with route-specific metadata before serving:\n\n- `/` — static site title and description\n- `/app/:id` — app-specific title, description, and OG image URL from the database\n- 404 — \"Page Not Found\" with appropriate status code\n\nThis gives crawlers and social platforms (Twitter, Slack, Discord) correct per-page metadata while the client-side React app hydrates normally from `\u003cdiv id=\"root\"\u003e`.\n\n### SQLite Database with Search\n\nApp metadata lives in an on-chain SQLite database ([`ic-rusqlite`](https://github.com/wasm-forge/ic-rusqlite)). Search queries run `LIKE` pattern matching across four columns — `app_name`, `title`, `author_name`, and `description` — with relevance-ordered results prioritizing name matches.\n\nDatabase migrations and SQL seed files are managed by `ic-sql-migrate` and baked into the canister at compile time.\n\n### All Assets Embedded in the Canister\n\nThe entire frontend build output — JS bundles, CSS, fonts, app screenshots in two resolutions (1500px and 300px), icons, and static images — is embedded into the WASM binary via `include_dir!` at compile time.\n\nStatic assets (content-hashed by Vite) are served with `Cache-Control: public, max-age=31536000, immutable` — a one-year cache with immutable hint. Dynamic responses (server-rendered HTML, OG images) use `Cache-Control: public, max-age=2592000` (30 days) and are re-certified on expiry.\n\n### AI-Powered Metadata Enrichment\n\nA build-time indexer crawls submitted app URLs, captures dual-resolution screenshots with Playwright, and enriches metadata using OpenAI or Anthropic. Results are cached incrementally so re-runs skip already-processed apps.\n\n## Quick Start\n\n### Prerequisites\n\n- icp-cli\n- Node.js / pnpm\n- OpenAI or Anthropic API key\n\n### 1. Start Local Network\n\n```bash\nicp network stop\nicp network start -d\n```\n\n### 2. Run the Indexer\n\n```bash\ncd indexer\npnpm install\npnpm exec playwright install chromium\ncp .env.example .env\n# Edit .env with your AI API key\npnpm dev\n```\n\nThis produces:\n- `server/src/seeds/seed_apps.sql` — SQL seed data\n- `indexer/images/` — Screenshot JPEGs\n\nSee [indexer/README.md](indexer/README.md) for details.\n\n### 3. Deploy\n\n```bash\n# Deploy locally (default)\nicp deploy server\n\n# Deploy to mainnet\nicp deploy server -e ic\n```\n\nThe deploy pipeline (defined in `icp.yaml`):\n1. `pnpm run build` — Vite builds the React frontend to `dist/`\n2. `cp -r indexer/images dist/images` — copies screenshots into the build output\n3. `cargo build` — compiles the Rust canister, embedding `dist/` (including images) via `include_dir!`\n4. `wasi2ic` — converts the WASM for IC deployment\n\n### 4. Access the App\n\n```\nhttp://\u003ccanister-id\u003e.localhost:8000/\n```\n\n### Development\n\nRun the Vite dev server for frontend development with hot reload:\n\n```bash\npnpm run dev\n```\n\nImages are proxied to the local canister automatically.\n\n## Tech Stack\n\n| Layer | Technology |\n|---|---|\n| Frontend | React, TanStack Router, Tailwind CSS, shadcn/ui |\n| Backend | Rust canister on the Internet Computer |\n| Routing | `ic-asset-router` (file-based, certified) |\n| Database | SQLite via `ic-rusqlite` |\n| Templating | MiniJinja (SSR meta tags + OG image SVG) |\n| OG Images | resvg + tiny-skia (SVG to PNG, on-chain) |\n| Indexer | Node.js, Playwright, OpenAI/Anthropic |\n\n## Project Structure\n\n```\n.\n├── server/                    # Rust canister\n│   ├── src/\n│   │   ├── lib.rs             # Canister init, HTTP handlers, Candid API\n│   │   ├── ogimage.rs         # OG image generation (SVG → PNG)\n│   │   ├── page/              # Database models \u0026 queries\n│   │   ├── routes/            # SSR route handlers (file-based)\n│   │   ├── includes/          # Embedded fonts, templates, background image\n│   │   └── seeds/             # SQL seed files (generated by indexer)\n│   ├── migrations/            # SQLite migrations\n│   ├── build.rs               # Route tree + migration codegen\n│   └── server.did             # Candid interface\n├── src/\n│   ├── components/            # React components\n│   ├── hooks/                 # React hooks\n│   ├── lib/                   # Constants and utilities\n│   └── routes/                # TanStack Router file-based routes\n├── indexer/                   # Build-time Node.js indexer\n│   ├── src/                   # Processing pipeline, AI enrichment, screenshots\n│   ├── submissions.csv        # Input data + cached AI results\n│   └── images/                # Cached screenshots\n├── icp.yaml                   # IC deployment config\n└── vite.config.ts             # Vite config with canister proxy\n```\n\n## API\n\n```candid\nservice : {\n    http_request : (HttpRequest) -\u003e (HttpResponse) query;\n    http_request_update : (HttpRequest) -\u003e (HttpResponse);\n    list_apps : () -\u003e (vec App) query;\n    get_app : (int64) -\u003e (GetAppResult) query;\n    search : (text) -\u003e (SearchResult) query;\n};\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkristoferlund%2Fpromptathon-showcase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkristoferlund%2Fpromptathon-showcase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkristoferlund%2Fpromptathon-showcase/lists"}