{"id":50467981,"url":"https://github.com/ckaznable/kabu","last_synced_at":"2026-06-01T08:32:43.613Z","repository":{"id":347262568,"uuid":"753906879","full_name":"ckaznable/kabu","owner":"ckaznable","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-05T02:52:40.000Z","size":160,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-05T04:36:41.510Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ckaznable.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-02-07T02:25:11.000Z","updated_at":"2026-05-05T02:52:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ckaznable/kabu","commit_stats":null,"previous_names":["ckaznable/kabu"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ckaznable/kabu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ckaznable%2Fkabu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ckaznable%2Fkabu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ckaznable%2Fkabu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ckaznable%2Fkabu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ckaznable","download_url":"https://codeload.github.com/ckaznable/kabu/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ckaznable%2Fkabu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33767435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-06-01T08:32:43.004Z","updated_at":"2026-06-01T08:32:43.605Z","avatar_url":"https://github.com/ckaznable.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kabu\n\nPersonal stock portfolio tracker. Tracks holdings, fetches live prices (global stocks via Finnhub, Taiwan stocks via TWSE/TPEX public feed), and extracts transactions from PDF financial documents using Gemini LLM.\n\n## Architecture\n\n```\nkabu/\n├── server/      — Axum HTTP server (API + static frontend)\n├── updater/     — One-shot price updater binary (for K8s CronJob)\n├── shared/      — Shared library (DB, models, config)\n├── typegen/     — TypeScript type generator (Rust → TS via specta)\n├── frontend/    — Vue 3 + Vite SPA\n└── migrations/  — SQL schema\n```\n\n## Configuration\n\nCopy `config.example.toml` to `config.toml` and fill in your API keys:\n\n```bash\ncp config.example.toml config.toml\n```\n\n### config.toml\n\n| Section    | Field           | Description                                         |\n|------------|-----------------|-----------------------------------------------------|\n| `server`   | `port`          | HTTP server port (default: `3000`)                  |\n| `server`   | `database_url`  | SQLite connection string (default: `sqlite:kabu.db`)|\n| `finnhub`  | `api_key`       | Finnhub API key (direct value)                      |\n| `finnhub`  | `api_key_env`   | Or: env var name to read Finnhub key from           |\n| `gemini`   | `api_key`       | Gemini API key (direct value)                       |\n| `gemini`   | `api_key_env`   | Or: env var name to read Gemini key from            |\n| `gemini`   | `model`         | Gemini model name (default: `gemini-2.5-flash-lite`)|\n| `exchange_rate` | `api_key`   | ExchangeRate-API key (direct value)                 |\n| `exchange_rate` | `api_key_env` | Or: env var name for ExchangeRate-API key         |\n| `exchange_rate` | `base`      | Base currency code (default: `USD`)                 |\n| `exchange_rate` | `currencies`| Target currency codes to store                       |\n| `pdf`      | `password`      | PDF decryption password (direct value)              |\n| `pdf`      | `password_env`  | Or: env var name to read PDF password from          |\n\nFor each secret, you can either:\n- Set the value directly in the config file, or\n- Set the `_env` variant to the name of an environment variable (e.g. `FINNHUB_API_KEY`), and export that variable in your shell\n\nConfig file path can be overridden with the `KABU_CONFIG` env var.\n\n### Required API Keys\n\n- **Finnhub** — Get a free key at https://finnhub.io/\n- **Gemini** — Get a key at https://aistudio.google.com/apikey\n- **ExchangeRate-API** — Get a key at https://www.exchangerate-api.com/\n\n### Taiwan Stock Symbols\n\n- Taiwan stocks are auto-routed to TWSE/TPEX source when symbol is numeric (e.g. `2330`) or ends with `.TW` / `.TWO`.\n- This source does not require an extra API key.\n\n## Development\n\n### Prerequisites\n\n- Rust (stable, edition 2024)\n- Node.js 18+\n- Make\n\n### Using Make\n\n```bash\nmake              # Build everything (frontend + server + updater)\nmake dev          # Build frontend, then run server\nmake dev-frontend # Vite dev server on http://localhost:5173 (proxies /api to :3000)\nmake dev-server   # Run Rust server directly\nmake typegen      # Regenerate TypeScript types from Rust models\nmake lint         # Run oxlint on frontend\nmake clean        # Remove all build artifacts\n```\n\n### Manual\n\n```bash\n# Backend\ncargo build\ncargo run -p kabu-server\ncargo run -p kabu-updater   # One-shot: fetches latest prices then exits\n\n# Frontend\ncd frontend\nnpm install\nnpm run dev\nnpm run build    # Production build → frontend/dist/\n```\n\n### Type Generation\n\nShared types between Rust and TypeScript are generated with specta:\n\n```bash\nmake typegen   # Outputs frontend/src/api/types.ts\n```\n\n## Production / K8s\n\nBuild two separate binaries for deployment:\n\n```bash\nmake build\n# or\ncargo build --release -p kabu-server\ncargo build --release -p kabu-updater\n```\n\n- `kabu-server` — serves the API and frontend static files from `frontend/dist/`\n- `kabu-updater` — one-shot binary, fetches prices for all tracked stocks then exits. Schedule with K8s CronJob for post-market updates.\n\nBoth binaries read from `config.toml` (or the path in `KABU_CONFIG`).\n\n### AArch64 Container Build\n\nFor an `arm64` image build on an `amd64` machine, use the helper script:\n\n```bash\n./scripts/build-kabu-aarch64.sh\n```\n\nDefaults:\n\n- package: `kabu-server`\n- image tag: `kabu-server:aarch64`\n- `TARGET_TRIPLE=aarch64-unknown-linux-gnu`\n\nExamples:\n\n```bash\n# Build the server image\n./scripts/build-kabu-aarch64.sh\n\n# Build the updater image\n./scripts/build-kabu-aarch64.sh kabu-updater\n\n# Override the output image tag\n./scripts/build-kabu-aarch64.sh kabu-server registry.example.com/kabu-server:latest\n```\n\nRequirements:\n\n- `cargo`\n- `rustup`\n- `cargo-zigbuild`\n- `zig`\n- `podman`\n- `npm` (only for `kabu-server`, to produce `frontend/dist`)\n\n## API Endpoints\n\n| Method   | Path                     | Description                                 |\n|----------|--------------------------|---------------------------------------------|\n| `GET`    | `/api/stocks`            | List all tracked stocks                     |\n| `POST`   | `/api/stocks`            | Add a stock to track                        |\n| `GET`    | `/api/stocks/{id}`       | Get stock details                           |\n| `PUT`    | `/api/stocks/{id}`       | Update stock quantity / cost                |\n| `DELETE` | `/api/stocks/{id}`       | Remove a stock                              |\n| `GET`    | `/api/portfolio/summary` | Portfolio overview with prices              |\n| `POST`   | `/api/pdf/upload`        | Upload PDF, extract transactions via Gemini |\n| `GET`    | `/api/transactions`      | List extracted transactions                 |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fckaznable%2Fkabu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fckaznable%2Fkabu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fckaznable%2Fkabu/lists"}