Kefine is a SvelteKit application for submitting solver tasks, tracking their execution state, and moving through authentication and payment flows. This repository also includes a small backend service called crater that accepts orders and serves order status updates.
- Frontend: SvelteKit + Vite + TypeScript
- Package manager: Bun
- End-to-end tests: Playwright
- Local backend: Crystal (
crater)
- Create a new task from the main screen
- Open a task detail page at
/task/:id - Track queued, active, payment-ready, and completed states
- Authenticate with a wallet or passkey
- Try the anonymous payment/deposit flow
- Reopen existing tasks from the shared order list
Install the following tools before starting:
misenerdctlwithnerdctl compose
Optional for local wallet auth:
- A Reown / WalletConnect project ID
If you just want to bring the full app up quickly, use nerdctl compose:
nerdctl compose up --buildThen open:
- Frontend:
http://localhost:5173 - Backend:
http://localhost:3001
This is the easiest way to run the frontend together with the local crater service.
The frontend production container is built from the repo root Containerfile and serves requests through Caddy on port 5173 by default.
Trust the repository once so mise can execute project tasks:
mise trustThen install the pinned toolchain:
mise installThis installs the pinned local versions of Bun, Node.js, and Crystal from mise.toml.
Then install project dependencies:
mise run installAll runtime configuration now lives in the repo-root kefine.config.json.
Main sections:
{
"app": {
"reownProjectId": "your_reown_project_id"
},
"origins": {
"primary": "https://lefine.pro",
"legal": "https://legal.lefine.pro",
"task": "https://tasks.lefine.pro"
},
"backend": {
"craterBaseUrl": "http://localhost:3001",
"exchangeBaseUrl": "https://lefine.pro/exchange",
"databaseUrl": "postgresql://kefine:kefine@localhost:5432/kefine"
},
"company": {
"legalName": "Lefine",
"email": "order@lefine.pro"
}
}Notes:
app.reownProjectIdis needed if you want wallet login/connect flows to work correctly.backend.craterBaseUrlis the crater base URL used by the SvelteKit proxy for order, payment, and passkey operations.backend.exchangeBaseUrlis the exchange base URL crater uses for user IDs and payment links.backend.databaseUrlis the Postgres connection string crater uses for orders, payment redemptions, passkey users, challenges, sessions, and outbox activity persistence.company.*controls the new/legal-informationcompany page. Empty optional fields are hidden automatically.
When a task repository is created, crater now seeds it with .lepos.rcl in the repository root.
You can use this config to control repository storage behavior:
[repository]
icon = "🧩"
issue_storage = "filesystem" # or "database"
default_branch = "main"
accept_pull_issues = true
accept_pull_patches = true
issue_root = ".meta/issues"
issue_readme_name = "README.org"
issue_file_name = "issue.org"
issue_attachments_dir = "attachments"
main_readme_path = ".meta/lefine.pro.org"
plan_document_path = "PLAN.org"
repository_readme = "Lefine repository metadata and task context"
reps_config_paths = "reps.rcl,reps.toml"
agent_system_prompt_path = ""
issue_storage = "filesystem"writes per-order notes and attachments toissue_root.issue_storage = "database"stores issue metadata/attachments in Postgres (repository_issue_artifacts) instead of repository files.iconstores repository icon metadata.default_branchcontrols branch name exposed to clients asrepository.defaultBranchwhen repository metadata is fetched.accept_pull_issuesandaccept_pull_patchescontrol which push branches create auto-run exchange issues.main_readme_pathconfigures the repository-level readme/metadata path.plan_document_pathconfigures the canonical execute plan file,PLAN.orgby default.issue_root,issue_file_name,issue_readme_name, andissue_attachments_dircontrol issue artifact layout.reps_config_pathslists repo-level config files to read (reps.rcl/reps.tomlby default).agent_system_prompt_pathsets a repository-level path for system prompt configuration.
Example reps.rcl file:
[reps]
repositories = ["repo-a", "repo-b"]
repository_config_path = "reps"
agent_system_prompt_path = "agents/system_prompt.md"
If reps.rcl is missing, reps.toml is checked next.
If .lepos.rcl is absent, crater uses default values and writes a new default .lepos.rcl on first seed.
Both /status/:id and /projects/:id/repository now expose repository .lepos.rcl settings under repository.leposConfig, so clients can inspect issueStorage and path settings for repositories they receive.
Project CI now runs through githooks scripts and all checks are executed in containers.
/.githooks/cicontains the current CI entrypoint/.githooks/pre-commitruns lint only/.githooks/pre-pushruns the full CI pipeline
Run full checks locally with:
./.githooks/ciTo make Git trigger hooks automatically:
git config core.hooksPath .githooksTo test against the hosted Lefine exchange instead of the local crater, use:
{
"backend": {
"craterBaseUrl": "http://localhost:3001",
"exchangeBaseUrl": "https://lefine.pro/exchange"
}
}With this setup:
- the frontend sends task, payment, and passkey requests only to the app's own crater-facing routes,
- the SvelteKit server forwards those requests to crater,
- crater persists orders, outbox activity, users, passkeys, challenges, sessions, and payment redemptions in Postgres, while still using
backend.exchangeBaseUrlfor exchange-facing URLs, - the UI keeps polling status updates through crater instead of talking to the exchange directly.
If crater is exposed on a public URL, do not leave the container defaults on localhost.
Use separate values for:
backend.craterBaseUrl: the public base URL crater uses when generatingorderId, actor IDs, inbox/outbox URLs, and related linksbackend.exchangeBaseUrl: the public exchange/app base URL crater uses for/pay/*, user IDs, and exchange-facing linksorigins.primary: the public frontend origin
Example when crater is hosted directly on https://lefine.pro and exchange is hosted on https://lefine.pro/exchange:
{
"backend": {
"craterBaseUrl": "https://lefine.pro",
"exchangeBaseUrl": "https://lefine.pro/exchange"
},
"origins": {
"primary": "https://lefine.pro",
"legal": "https://lefine.pro",
"task": "https://lefine.pro"
}
}The frontend can redirect different page groups onto different public origins without changing route paths.
origins.primary: primary site origin, used for/origins.legal: legal pages origin for/privacy,/terms,/legal-informationorigins.task: task flow origin for/create,/task/*,/order/*,/payment/*,/pay/*,/status*,/passkeys/*,/api/kefine/*
Example:
{
"origins": {
"primary": "https://lefine.pro",
"legal": "https://legal.lefine.pro",
"task": "https://tasks.lefine.pro"
}
}With that setup:
https://lefine.pro/privacyredirects tohttps://legal.lefine.pro/privacyhttps://lefine.pro/task/<id>redirects tohttps://tasks.lefine.pro/task/<id>- local development on
localhostis not redirected
For this setup:
- new
orderIdvalues are emitted under the configured public crater/exchange URLs instead of local defaults - payment and exchange-facing links are emitted under the configured exchange URL
- the frontend proxies requests to the hosted crater at
https://lefine.pro
The recommended local workflow is now a single mise command:
mise run devThis command:
- ensures frontend dependencies are installed,
- starts Postgres via
nerdctl compose, - waits until the database is ready,
- starts
craterin a container onhttp://localhost:3001, - starts the SvelteKit frontend on
http://localhost:5173.
You can also run just mise run, because the default task maps to dev.
mise run frontendruns only the frontendmise run backendruns the backend in a container and ensures Postgres is upmise run backend:localruns the backend directly with local Crystalmise run backend:downstops the backend containermise run db:upstarts only Postgresmise run db:downstops only Postgresmise run installrefreshes all project dependencies
frps is intended to run on the public server and frpc on your local machine.
Server target:
- host:
193.32.177.159 - control port:
7000 - internal FRP HTTP vhost port:
8080 - public HTTPS entrypoint:
443
Client config template lives at scripts/frpc.dev-proxy.toml.
The tunnel is configured so both the frontend and crater live behind the same public origin:
- frontend traffic goes to local
127.0.0.1:5173 craterpaths on the same domain go to local127.0.0.1:3001- app runtime config in
kefine.config.jsonpoints bothorigins.*andbackend.*BaseUrltohttps://dev-proxy.col.pub - TLS is terminated on the public server by Caddy running under
nerdctl compose, which proxies to local FRP HTTP routing on port8080
Run it like this after starting the frontend locally:
export FRP_TOKEN='your_server_token'
mise run tunnelOr run the local stack and the tunnel together:
export FRP_TOKEN='your_server_token'
mise run dev:proxyImportant:
dev-proxy.col.pubmust have anArecord pointing to193.32.177.159- until DNS is added, the tunnel will connect but the domain itself will not resolve publicly
If you do not want to use the one-command mise flow, you still have two practical backend options.
Option A: run only the backend in a container
nerdctl compose up --build craterOption B: run the backend directly with Crystal
cd crater
shards install
crystal run src/crater.crThe backend listens on http://localhost:3001 by default.
From the repository root:
bun run devOpen the app at:
http://localhost:5173
- Open
http://localhost:5173. - Enter a task description in the main input, for example:
Deploy my production appNeed access to TelegramOptimize an algorithm
- Submit the task.
What happens next:
- The app creates an order through
crater - You are redirected to
/task/:id - The task detail screen shows solver, ETA, price, and current status
- Go back to the main page
- Use the left/shared order list
- Click an existing order to reopen its task page
The app also keeps task routes stable, so reloading /task/:id should return you to the same task.
The task detail flow supports several auth paths before payment:
- Wallet
- Passkey
- Anonymous
- Requires
app.reownProjectIdinkefine.config.json - Lets you connect a wallet via Reown/AppKit
- Best option if you want to exercise the wallet-based payment path
- Uses crater-backed routes under
/passkeys/* - Crater stores passkeys, sessions, and exchange user accounts in
.data/exchange-state.jsonby default - Useful for testing passwordless authentication locally
Important:
- Passkeys depend on browser WebAuthn support
- Browser/device behavior may differ between localhost, custom domains, and HTTPS setups
- Lets you continue without linking an account first
- Exposes the deposit/payment flow in the UI
Once a task reaches the payment-ready stage, the UI lets you continue through one of the supported payment paths.
In local development, the most useful thing to verify is that:
- the payment screen opens,
- the deposit dialog can be opened,
- the UI progresses to the result-ready state.
After the payment/deposit step, the UI can reveal a result panel and a save action. This is the final stage of the main task journey.
bun run checkbun run lintbun run testbun run test:e2e- The frontend assumes crater is reachable on port
3001unlessbackend.craterBaseUrloverrides it. - Exchange-facing IDs and payment links are derived from
backend.exchangeBaseUrl. - Passkey and exchange account data are stored by crater in
.data/exchange-state.json; deleting that file resets local state. - The backend default configuration is development-friendly and should work locally without additional setup.
To create a production build:
bun run buildTo preview it locally:
bun run previewTo build the frontend production image directly:
docker build -f Containerfile -t kefine-frontend .To run it on the default Caddy port:
docker run --rm -p 5173:5173 kefine-frontendCheck that:
crateris running onlocalhost:3001backend.craterBaseUrlinkefine.config.jsonpoints to the correct crater URL- your browser can access both the frontend and backend ports
Check that:
app.reownProjectIdis set- you updated
app.reownProjectIdinkefine.config.json - you restarted the dev server after changing the config file
Check that:
- your browser supports WebAuthn/passkeys
- you are testing on a valid local origin
.data/exchange-state.jsonis writable by crater
.
├── src/ # SvelteKit frontend
├── crater/ # Crystal backend service
├── e2e/ # Playwright tests
├── packages/ # Shared local packages
├── Dockerfile # Frontend container
└── docker-compose.yml # Full local stack