Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
24d831c
feat(server): single-binary skeleton with three listeners (#87)
manish-9245 Oct 24, 2026
406100b
chore(review): CodeRabbit rules for money path, security, customer pa…
manish-9245 Oct 24, 2026
651d90a
feat(server): embedded SQLite with migrations (#88)
manish-9245 Oct 24, 2026
b69d422
ci: lint, race tests, coverage gate, fuzz and cross-builds (#89)
manish-9245 Oct 24, 2026
38a0388
test: shared test helpers, injectable clock and ids (#90)
manish-9245 Oct 24, 2026
11cbc5e
feat(security): envelope encryption for secrets and API keys (#91)
manish-9245 Oct 24, 2026
d724237
feat(server): receiving accounts (multiple UPI IDs per shop) (#92)
manish-9245 Oct 24, 2026
d8061c6
feat(security): hash-chained ledger and kaasu verify (#93)
manish-9245 Oct 24, 2026
a4f23aa
feat(api): orders API with idempotency (#94)
manish-9245 Oct 24, 2026
0d76b6a
feat(pool): unique amount pool with queue fallback (#95)
manish-9245 Oct 24, 2026
433e8b8
feat(states): order state machine with ledgered transitions (#96)
manish-9245 Oct 24, 2026
910bca2
fix(server): never reuse a held amount, hide links of queued orders (…
manish-9245 Oct 24, 2026
8c83ec3
feat(alerts): evidence store and cross-source dedupe (#99)
manish-9245 Oct 24, 2026
c9355f9
feat(alerts): bank alert parsers with golden fixtures and fuzzing (#100)
manish-9245 Oct 24, 2026
0315e6b
feat(alerts): device pairing and signed ingest (#101)
manish-9245 Oct 24, 2026
4f7c4ea
feat(alerts): matcher, scorer and policy (#102)
manish-9245 Oct 24, 2026
1fa6b9d
feat(alerts): IMAP email observer with DKIM verification (#103)
manish-9245 Oct 24, 2026
e897206
feat(server): persistent background scheduler (#104)
manish-9245 Oct 24, 2026
288084d
feat(webhooks): signed webhooks through a transactional outbox (#105)
manish-9245 Oct 24, 2026
88477c5
feat(refunds): refund log with paid-amount limits (#106)
manish-9245 Oct 24, 2026
393c8b5
feat(recon): statement import and reconciliation buckets (#107)
manish-9245 Oct 24, 2026
4c3812c
feat(admin): TOTP sign-in, sessions, lockout and re-auth (#108)
manish-9245 Oct 24, 2026
f7dc46f
feat(http): security headers, limits, recovery and rate limiting (#109)
manish-9245 Oct 24, 2026
b83d603
feat(ops): redacted logs, request ids and Prometheus metrics (#110)
manish-9245 Oct 24, 2026
86f2a36
ci: signed release workflow, security scans and reproducibility check…
manish-9245 Oct 24, 2026
117afcc
feat(dev): payment simulator core and outbound guard (#112)
manish-9245 Oct 24, 2026
cda76a7
feat(design): tokens, icons and a generated stylesheet (#113)
manish-9245 Oct 24, 2026
a1dd62b
feat(i18n): catalogue for eleven languages, Indian money formatting (…
manish-9245 Oct 24, 2026
003eac8
feat(web): customer pay page (#115)
manish-9245 Oct 24, 2026
8963020
feat(web): payment confirmed page and receipt (#116)
manish-9245 Oct 24, 2026
df5f96e
feat(links): payment links, counter links and the counter sign (#117)
manish-9245 Oct 24, 2026
4d17c27
feat(web): checkout snippet and shared webhook verification vectors (…
manish-9245 Oct 24, 2026
6cc70db
feat(voice): spoken confirmations, only for confirmed payments (#119)
manish-9245 Oct 24, 2026
96db1b4
feat(admin): merchant dashboard, first slice (#120)
manish-9245 Oct 24, 2026
8a1b8db
feat(review): review queue with owner-only, fresh-auth decisions (#121)
manish-9245 Oct 24, 2026
0c1580f
feat(setup): first-run wizard steps and the one-rupee test payment (#…
manish-9245 Oct 24, 2026
59004e8
feat(paisu): mood rules for merchant screens (#123)
manish-9245 Oct 24, 2026
068cec7
test(a11y): template and stylesheet checks, pay page heading (#124)
manish-9245 Oct 24, 2026
a1f5ef7
feat(infra): installer, hardened systemd unit, Docker image, doctor a…
manish-9245 Oct 24, 2026
128ae8b
feat(infra): Railway template configuration and deploy guide (#127)
manish-9245 Oct 24, 2026
e66d6e2
docs(legal): privacy and trademark drafts, bank terms guidance (#128)
manish-9245 Oct 24, 2026
0929ab7
ci: conventional PR titles, area labels and contributor conventions (…
manish-9245 Oct 24, 2026
d78915a
test(security): attack suite over real HTTP; fix date-only alert timi…
manish-9245 Oct 24, 2026
7ca4f2e
feat(ai): optional provider layer with masking, schema checks, budget…
manish-9245 Oct 24, 2026
787126d
feat(ai): structured extraction for unknown alert formats (#132)
manish-9245 Oct 24, 2026
75860ce
feat(ai): read-only questions over fixed queries; read pool refuses w…
manish-9245 Oct 24, 2026
4fb6ccb
feat(ai): statement mapping inferred and checked on the whole file (#…
manish-9245 Oct 24, 2026
d664206
feat(ai): deterministic hints, phrasing only for detected hints, fixe…
manish-9245 Oct 24, 2026
50b5ce4
chore(deps): bump cloudflare/wrangler-action from 3.15.0 to 4.1.3 (#80)
dependabot[bot] Oct 24, 2026
27569ab
chore(deps): bump actions/checkout from 4.4.0 to 7.0.1 (#81)
dependabot[bot] Oct 24, 2026
bcb7e35
chore(deps): bump actions/upload-artifact from 4.6.2 to 7.0.2 (#82)
dependabot[bot] Oct 24, 2026
c8a2d77
feat(infra): AWS shop module, encrypted versioned bucket and log grou…
manish-9245 Oct 23, 2026
b4f3663
feat(integrations): WooCommerce gateway with integer-paise amounts an…
manish-9245 Oct 23, 2026
a9075e2
ci(woocommerce): fix duplicate version comment
manish-9245 Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# CodeRabbit review rules for Kaasu. Source of truth for review behaviour; AGENTS.md holds the project rules.
language: en-GB
early_access: false

reviews:
profile: assertive
request_changes_workflow: false
high_level_summary: true
high_level_summary_placeholder: "@coderabbitai summary"
review_status: true
commit_status: true
collapse_walkthrough: false
poem: false
auto_review:
enabled: true
drafts: false
auto_incremental_review: true
auto_pause_after_reviewed_commits: 0
base_branches:
- ".*"

path_filters:
- "!**/*.golden"
- "!**/*.lock"
- "!**/package-lock.json"
- "!**/go.sum"
- "!**/*.png"
- "!**/*.webp"

path_instructions:
- path: "internal/match/**"
instructions: >-
MONEY PATH. Only this package may confirm a payment. Check: amounts are int64 paise (never float),
an auto-confirm needs an exact amount AND an authentic source AND score >= 80 AND an order under the
account limit, late or closed orders go to review, and Process is idempotent. Flag any new code path
that can reach confirmed without these checks. Every behaviour change needs a test.
- path: "internal/states/**"
instructions: >-
MONEY PATH. The transition table is the only place order state changes. Check every new edge against the
table in the card, that each transition writes a ledger row in the same transaction, and that refunds are
the only release of a held amount.
- path: "internal/ledger/**"
instructions: >-
MONEY PATH. The ledger is append-only and hash-chained. Flag anything that updates or deletes rows,
changes the hash inputs, or stores raw alert text or full payer identifiers.
- path: "internal/ingest/**"
instructions: >-
SECURITY. Every request is signed over the body hash, timestamp and nonce; the window is 60 seconds;
replays get 409; every failure is the same 401 with no detail. Flag any path that accepts an unsigned
or stale request, and any use of == on secrets.
- path: "internal/parsers/**"
instructions: >-
MONEY PATH. Amounts are integer paise parsed from the digit string. Flag ParseFloat or any float
arithmetic on money, any parser that accepts a debit, and any fixture with real customer data.
- path: "internal/email/**"
instructions: >-
SECURITY. DKIM is verified on the raw bytes; the signing domain must be an allow-listed bank domain
(exact or subdomain); the From address must align. Flag any path that trusts a message without DKIM.
- path: "internal/secrets/**"
instructions: >-
SECURITY. Secrets are envelope-encrypted (XChaCha20-Poly1305); the master key never lives in the
database; API keys are stored as SHA-256 hashes and shown once. Flag plaintext storage or logging.
- path: "internal/adminauth/**"
instructions: >-
SECURITY. Sessions are stored as hashes, cookies are HttpOnly, SameSite=Strict and Secure outside local
http; TOTP codes are single-use; lockout and the same reply for unknown names must hold. Flag any change
that weakens these.
- path: "internal/web/**"
instructions: >-
CUSTOMER PAGES. No third-party scripts, no inline scripts, no 'failed' copy while an outcome is
unknown, and no Paisu on customer pages. Text comes from the i18n catalogue, never hard-coded.
- path: "i18n/**"
instructions: >-
Every key must exist in every language with the same placeholders. Machine drafts must stay listed in
i18n/REVIEW.md. Do not mark a translation reviewed.
- path: "**/*.md"
instructions: >-
Documentation. Flag em dashes (U+2014) and claims the code does not back up. Keep copy plain.
- path: "web/**"
instructions: >-
Website. No em dashes. No emoji used as icons. No tech-stack wording on the landing page; talk about impact.
- path: "**/*.go"
instructions: >-
Go: stdlib first. Wrap errors with context. No package-level mutable globals. Table-driven tests. Money is
int64 paise. Flag any network call in tests.

tools:
golangci-lint:
enabled: false
gitleaks:
enabled: true
semgrep:
enabled: true
ast-grep:
enabled: false

finishing_touches:
docstrings:
enabled: false
unit_tests:
enabled: false

chat:
auto_reply: true

knowledge_base:
opt_out: false
learnings:
scope: auto
code_guidelines:
enabled: true
filePatterns:
- "AGENTS.md"
- "CONTRIBUTING.md"
- "architecture.md"
- "design.md"
- "SECURITY.md"
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.git
.agents
*.db
.kaasu-dev
bin
web
node_modules
secrets
18 changes: 18 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
area:server:
- changed-files:
- any-glob-to-any-file: ["internal/**", "cmd/**"]
area:web:
- changed-files:
- any-glob-to-any-file: ["web/**", "internal/web/**"]
area:docs:
- changed-files:
- any-glob-to-any-file: ["**/*.md", "docs/**"]
area:security:
- changed-files:
- any-glob-to-any-file: ["internal/secrets/**", "internal/adminauth/**", "internal/ingest/**", "SECURITY.md"]
area:i18n:
- changed-files:
- any-glob-to-any-file: ["i18n/**"]
area:infra:
- changed-files:
- any-glob-to-any-file: [".github/**", "deploy/**", "Dockerfile", "compose.yaml", "install.sh"]
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
language: [javascript-typescript, actions]
# Add `go`, `java-kotlin` and `swift` when those code trees land.
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: github/codeql-action/init@4bfa242aa49c5f7c6770b98670d354a7dfac05f8 # v3
with: { languages: '${{ matrix.language }}' }
- uses: github/codeql-action/analyze@v3
- uses: github/codeql-action/analyze@4bfa242aa49c5f7c6770b98670d354a7dfac05f8 # v3
with: { category: '/language:${{ matrix.language }}' }
63 changes: 63 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: go
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: go-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- name: gofmt
run: test -z "$(gofmt -l .)" || (gofmt -l . && exit 1)
- run: go vet ./...
- run: go run honnef.co/go/tools/cmd/staticcheck@2026.2.2 ./...
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- run: make test-cover
- run: |
go tool cover -func=coverage.out | tail -1 | sed 's/^/Total coverage: /' >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: { name: coverage, path: coverage.out, retention-days: 7 }
fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- run: scripts/fuzz.sh 30s
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm64 }
- { goos: darwin, goarch: amd64 }
- { goos: darwin, goarch: arm64 }
- { goos: windows, goarch: amd64 }
- { goos: windows, goarch: arm64 }
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- run: go build -trimpath -ldflags "-s -w" -o kaasu-${{ matrix.goos }}-${{ matrix.goarch }} ./cmd/kaasu
env: { CGO_ENABLED: '0', GOOS: '${{ matrix.goos }}', GOARCH: '${{ matrix.goarch }}' }
repro:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- run: scripts/repro-check.sh
15 changes: 15 additions & 0 deletions .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: labeler
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
15 changes: 15 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: pr-title
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
contents: read
jobs:
title:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- run: scripts/check-pr-title_test.sh
- run: scripts/check-pr-title.sh "$PR_TITLE"
env:
PR_TITLE: ${{ github.event.pull_request.title }}
60 changes: 60 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: release
on:
push:
tags: ['v*']
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm64 }
- { goos: darwin, goarch: amd64 }
- { goos: darwin, goarch: arm64 }
- { goos: windows, goarch: amd64 }
- { goos: windows, goarch: arm64 }
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- name: Build
env:
CGO_ENABLED: '0'
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
VERSION: ${{ github.ref_name }}
run: |
ext=""; [ "$GOOS" = windows ] && ext=".exe"
go build -trimpath -buildvcs=false -ldflags "-s -w -X main.version=$VERSION" -o "kaasu-$GOOS-$GOARCH$ext" ./cmd/kaasu
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: bin-${{ matrix.goos }}-${{ matrix.goarch }}
path: kaasu-*
publish:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # keyless cosign signing
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with: { path: dist, merge-multiple: true }
- uses: anchore/sbom-action@55dc4ee22412511ee8c3142cbea40418e6cec693 # v0.17.8
with: { path: dist, format: spdx-json, output-file: dist/kaasu.spdx.json }
# The SBOM is listed in SHA256SUMS, and SHA256SUMS is signed, so the SBOM is authenticated too.
- name: Checksums
working-directory: dist
run: sha256sum kaasu-* kaasu.spdx.json > SHA256SUMS
- uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
- name: Sign
working-directory: dist
run: |
for f in kaasu-* SHA256SUMS; do cosign sign-blob --yes --output-signature "$f.sig" --output-certificate "$f.pem" "$f"; done
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
with:
draft: true # a person publishes the draft after checking it
generate_release_notes: true
files: dist/*
22 changes: 22 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: security
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '23 4 * * 1'
permissions:
contents: read
jobs:
govulncheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with: { go-version-file: go.mod }
- run: go run golang.org/x/vuln/cmd/govulncheck@v1.2.0 ./...
pins:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: scripts/check-pins.sh
12 changes: 6 additions & 6 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ jobs:
runs-on: ubuntu-latest
defaults: { run: { working-directory: web } }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: { node-version: 24, cache: npm, cache-dependency-path: web/package-lock.json }
- run: npm ci
- run: npm test
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: { name: site, path: web/dist, retention-days: 7 }
deploy:
needs: test
# Secrets are not available to pull requests from forks.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with: { name: site, path: web/dist }
- name: Deploy to Cloudflare
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4.1.3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/woocommerce.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: woocommerce
on:
pull_request:
paths: ['integrations/woocommerce/**']
push:
branches: [main]
paths: ['integrations/woocommerce/**']
permissions:
contents: read
jobs:
php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # v2
with: { php-version: '8.2' }
- run: find integrations/woocommerce -name '*.php' -print0 | xargs -0 -n1 php -l
- run: php integrations/woocommerce/tests/run.php
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bin/
*.db
*.db-wal
*.db-shm
.agents/plans/
coverage.out

# Terraform local state and providers
**/.terraform/
*.tfstate*
Loading
Loading