- TypeScript 58.8%
- HTML 25.6%
- Svelte 14.5%
- Dockerfile 0.4%
- CSS 0.3%
- Other 0.3%
|
All checks were successful
Build and Push Docker Image / build (push) Successful in 5m23s
|
||
|---|---|---|
| .forgejo/workflows | ||
| messages | ||
| project.inlang | ||
| src | ||
| static | ||
| .dockerignore | ||
| .env.example | ||
| .envrc | ||
| .gitignore | ||
| .npmrc | ||
| bun.lock | ||
| CLAUDE.md | ||
| CREDITS.md | ||
| Dockerfile | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE | ||
| mcp-servers.json | ||
| package.json | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
EduFeed Chat Bot
AI chat interface for educational resources using the AMB (Allgemeines Metadatenprofil für Bildungsressourcen) metadata standard on Nostr.
Architecture
┌─────────────────┐
│ amb-relay │ ◀── AMB educational
│ (AMB data store)│ resource events
└────────▲────────┘
│ queries AMB events
│
┌─────────────────┐ HTTP ┌─────────────────┐
│ edufeed-chat │────────▶│ amb-mcp │
│ (this app) │ MCP │ (MCP server) │
└────────┬────────┘ └─────────────────┘
│
│ NIP-44 encrypted chat history
│ + encrypted BYOK settings
▼
┌─────────────────┐
│ Nostr relays │
│ (user's relays) │
└─────────────────┘
Data flows:
- edufeed-chat ↔ MCP servers: Tool calls via Streamable HTTP or stdio MCP transports
- amb-mcp → amb-relay: Queries AMB events (educational resource metadata)
- edufeed-chat → user's relays: Stores NIP-44 encrypted conversation history and BYOK settings (Kind 30078)
Tech Stack
| Category | Technologies |
|---|---|
| Frontend | SvelteKit, Svelte 5 (runes), Tailwind CSS 4, DaisyUI 5 |
| LLM SDK | Vercel AI SDK v6 (ai, @ai-sdk/svelte, @ai-sdk/anthropic, @ai-sdk/openai) |
| LLM Providers (BYOK) | Anthropic, OpenAI, PayPerQ, Routstr, Ollama (any OpenAI-compatible endpoint) |
| Protocol | MCP (Model Context Protocol) — Streamable HTTP and stdio transports |
| Nostr | applesauce-core, applesauce-relay, applesauce-signers, applesauce-loaders |
| Package Manager | bun |
BYOK (Bring Your Own Key)
Users supply their own LLM credentials through the in-app settings modal — no API keys live in the server environment. The config (provider, baseURL, apiKey, model) is:
- Cached in
sessionStorage(survives refresh, cleared on tab close) - Persisted as a NIP-44 encrypted Kind 30078 event (
dtag:edufeed.settings) for cross-device sync - Sent per-request in the body to
/api/chat, which instantiates the provider on the fly
Related Projects
- amb-mcp - MCP server providing educational resource tools (search, browse subjects, etc.)
- amb-relay - Nostr relay with AMB event indexing and query support
Getting Started
Prerequisites
- bun package manager
- A Nostr signer (e.g. a NIP-07 browser extension) — used to encrypt/sign chat history and settings
- An LLM API key (configured in-app via the BYOK settings modal — not in
.env)
Setup
-
Clone the repository
-
Install dependencies:
bun install -
Copy the environment template:
cp .env.example .env -
Configure your
.env:# MCP Server Configuration (Streamable HTTP transport) AMB_MCP_URL=https://mcp.amb.edufeed.org/mcp AMB_MCP_BEARER_TOKEN=<token> # Nostr relays PUBLIC_BOOTSTRAP_RELAYS=wss://purplepag.es,wss://relay.damus.io,wss://relay.nostr.band,wss://nos.lol PUBLIC_CHAT_RELAYS=ws://localhost:10547 -
Start the dev server:
bun dev -
Open the app, sign in with your Nostr signer, then open the settings modal to configure your LLM provider and API key.
MCP Server Configuration
MCP servers are configured in mcp-servers.json. The registry connects to all enabled servers on startup, aggregates their tools, and prefixes tool names with the server id (e.g. amb__search_resources) to avoid collisions.
Two transports are supported:
Streamable HTTP
{
"id": "amb",
"name": "AMB Educational Resources",
"description": "Search and browse educational resources",
"url": "${AMB_MCP_URL}",
"bearerToken": "${AMB_MCP_BEARER_TOKEN}",
"enabled": true
}
stdio
{
"id": "nostrbook",
"name": "Nostrbook",
"description": "Reference docs for the Nostr protocol, NIPs, event kinds, and tags",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@nostrbook/mcp@latest"],
"enabled": true
}
The ${VAR} syntax references environment variables, keeping secrets in .env.
Adding a New MCP Server
- Add an entry to
mcp-servers.json(HTTP or stdio as above). - If it uses
${VAR}references, add the corresponding values to.env. - Restart the dev server.
Nostr Data Model
Conversations and BYOK settings are stored as NIP-78 (Kind 30078) events, NIP-44 self-encrypted:
d tag |
Content |
|---|---|
edufeed.chat.conv.{uuid} |
Serialized UIMessage[] for a single conversation |
edufeed.settings |
{ provider, baseURL, apiKey, model } BYOK config |
User relay preferences are read from NIP-65 (Kind 10002); PUBLIC_CHAT_RELAYS is used as fallback.
Commands
bun dev # Start dev server
bun run build # Production build
bun run check # Type check
bun run preview # Preview production build
bun test # Run tests
License
Unlicense - Public Domain