* feat(bughunt-fix): add a circuit breaker for systematically failing runs A fix run had no abort condition. If something was systematically wrong - the operator on the wrong branch, a broken test runner, ledger coordinates gone stale after a rebase - it worked through every cluster, spending a high-effort agent on each, and only reported the wreckage at the end. Two trip points, because there are two distinct failure signals: - after the fix stage, a high blocked rate means the fixing itself is failing. Proving each of those costs a serial agent per cluster and cannot succeed, so phase 3 is skipped entirely. - inside the prove loop, a high revert-proof failure rate means the proving is failing. Break rather than attempt the rest. `declined` never counts as a failure - it is a judgement the fix prompt explicitly invites, and a run where several findings are correctly declined is a good run. Both points require a minimum number of attempts first, because "50% of two" is noise. Clusters never reached are marked blocked with a rationale naming the breaker, so nothing is left reported as fixed with no commit behind it, and the gate still runs over whatever committed before the trip. proveAttempts is incremented before the ok check so successes land in the denominator; inside the failure branch the ratio would be failures-over-failures and trip on the first failed cluster at any threshold. Verified with 6 new harness scenarios (21 -> 27, all green, bughunt.harness.mjs untouched at 21). The guard was also proved load-bearing: with the threshold temporarily raised to an unreachable 1.1, f16 runs all four clusters instead of stopping at three and f20 produces no breaker report - both fail for the reason the guard exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(bughunt): attribute confirmed findings to the finder that produced them The dual-model panel unions its two finders rather than voting between them, so the second model's entire value is what it finds alone - and the union threw that away, leaving no way to tell whether sonnet earns its cost. Tag each finding with its panel slot. Because dedupe keeps the first occurrence and opus is slot 0, a confirmed finding tagged sonnet is one opus missed, which is exactly the number that decides the question. The run logs the split. Three details worth naming: - the tag is taken from the panel slot, not from the position in the surviving list. Filtering the nulls out before reading the index shifts sonnet into slot 0 whenever opus dies and mislabels its finds as opus - precisely when the attribution matters most. - the tag is stripped in verifyPrompt, not at its two call sites, so every caller routes through the guard. The verifier prompt says "another model" on purpose; naming it is an authority cue that erodes refute-by-default. - dropping to a single finder would also weaken convergence, since a round only counts as dry when the full panel reported. The skill records this next to the count so the decision is made with both halves in view. Verified with 4 new harness scenarios (21 -> 25). The dead-opus case is the load-bearing one: it fails against the naive filter-then-index form. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
OwnCord
A self-hosted chat app I build for me and my friends — text channels, voice and video, and a server you actually own.
Alpha, and a hobby project. This is something I build for fun and run for a small group of friends. It isn't a product, there's no support, and it isn't production-ready. Expect rough edges, rapid changes, and the occasional breaking change.
Don't use it for anything sensitive.
It's a Go server plus a Tauri desktop client: real-time messaging, voice/video via LiveKit, file sharing, and a web admin panel. Run the server on a spare box or a VPS, hand your friends an invite code, and that's the whole thing.
How it's built
Most of the implementation is generated with AI tooling, with quality held up by automated checks — CI, tests, linting — and by me and my friends actually using it. That keeps iteration fast, and it also means behaviour can change quickly between releases.
What works right now
| Area | Status |
|---|---|
| Core chat flow | Working in alpha |
| Voice/video | Working in alpha |
| Admin panel | Working in alpha |
| Security hardening | Ongoing review passes; findings and their statuses are tracked in the dated audits in docs/ (see the Docs Index below) |
Platform Support (Current Releases)
| Component | Windows x64 | Linux x64 | Linux ARM64 |
|---|---|---|---|
| Server binary | Yes | Yes | Not yet |
| Desktop client | Yes (NSIS installer) | Yes (AppImage, .deb) | Yes (AppImage, .deb) |
| Docker server | N/A | Build from source (compose) | Not yet |
Start Here
- New user quick path: docs/quick-start.md
- Linux Docker deployment: docs/deployment.md
- Remote access without router config: docs/tailscale.md
- Manual router/network setup: docs/port-forwarding.md
Quick Start
Option A: Prebuilt binaries
- Download assets from Releases (binaries, checksums, signatures, and a full source snapshot per release).
- Run the server binary:
- Windows:
chatserver.exe - Linux:
./chatserver
- Windows:
- Open
https://localhost:8443/adminand complete the setup wizard — it creates your Owner account and configures the server for you (settings are saved toconfig.yamlautomatically). - Generate invite codes in the admin panel and share them with friends.
Option B: Docker (Linux server)
cd Server
cp .env.example .env
cp livekit.yaml.example livekit.yaml
# Edit both files before starting
docker compose up -d
See the full setup guide in docs/deployment.md.
The client uses TOFU (Trust On First Use) for self-signed certificates: it prompts once, then pins the certificate for future connections.
What OwnCord Already Has
- Real-time channels and direct messages over WebSocket
- Voice/video channels via LiveKit — the LiveKit server binary is downloaded and managed for you
- Invite-only registration and role-based permissions
- Web admin panel with logs, backups, and update tooling
- File uploads and inline media rendering
- TOTP 2FA support and API rate limiting
- Desktop client auto-update with signature verification
- WASM plugin system (slash commands; sandboxed, default-disabled — enable via
plugins.enabledand build with-tags wazero) - GIF picker — off by default; each server supplies its own
Klipy key via
gif.api_key(setup)
See deeper feature and architecture docs in docs/architecture/ and docs/protocol.md.
Architecture
Two main components:
- Go server (REST API, WebSocket hub, SQLite, admin panel)
- Tauri v2 desktop client (Rust backend + TypeScript frontend)
+---------------------+ +---------------------+
| OwnCord Client | | OwnCord Server |
| (Tauri v2) | | (Go) |
| | | |
| +---------------+ | WSS | +---------------+ |
| | Chat UI |--+------->| | WebSocket Hub| |
| +---------------+ | | +---------------+ |
| +---------------+ | HTTPS | +---------------+ |
| | REST Client |--+------->| | REST API | |
| +---------------+ | | +---------------+ |
| +---------------+ | LiveKit | +---------------+ |
| | Voice/Video |--+------->| | LiveKit SFU | |
| +---------------+ | | +---------------+ |
+---------------------+ | +---------------+ |
| | SQLite DB | |
| +---------------+ |
+---------------------+
Build and Test
Prerequisites
- Go 1.26+
- Node.js 20+
- Rust stable (client builds)
Build from source
# Server (Windows)
cd Server
go build -o chatserver.exe -ldflags "-s -w -X main.version=1.2.0-alpha.2" .
# Server (Linux)
cd Server
CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.2" .
# Client
cd Client/tauri-client
npm install
npm run tauri build
Core verification commands
# Server
cd Server
go test ./...
# Client
cd Client/tauri-client
npm run typecheck
npm run lint
npm test
For the full command set, use docs/contributing.md.
Configuration
On first run, the server generates config.yaml and a local data/ directory:
data/
├── chatserver.db
├── certs/
├── uploads/
└── backups/
Key options include TLS mode, upload limits, LiveKit settings, and admin CIDR restrictions. See docs/server-configuration.md.
Security and Vulnerability Reporting
- For vulnerabilities, use GitHub Security Advisories (private disclosure flow).
- Do not open public issues for security bugs.
- Read full policy and hardening notes in docs/security.md.
Update Signing Notes (Maintainers)
Client and server update signing keys are intentionally separate.
Required Actions secrets for release signing:
TAURI_SIGNING_PRIVATE_KEYTAURI_SIGNING_PRIVATE_KEY_PASSWORDSERVER_UPDATE_SIGNING_PRIVATE_KEYSERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD
When rotating the server updater key, update Server/updater/server_update_public_key.txt and use staged rollover for live fleets.
Docs Index
- docs/quick-start.md
- docs/deployment.md
- docs/livekit-setup.md
- docs/port-forwarding.md
- docs/tailscale.md
- docs/architecture/ — system blueprints (diagrams + flows)
- docs/audit-2026-08-04-docs-and-coverage.md — latest full audit (docs accuracy, UX flow coverage, test runs)
- docs/audit-2026-08-04.md — latest security review
- docs/audit-2026-07-19.md — architecture & spec-conformance audit
- docs/api.md
- docs/protocol.md
- docs/schema.md
- docs/architecture/client.md — client architecture (replaces client-architecture.md)
- docs/architecture/ux/ — client UX specification (target-state flows, per-view states, event→reaction maps)
- docs/server-configuration.md
- docs/credential-storage.md
- docs/mcp-introspect.md — dev-only MCP server for introspecting a running instance
- docs/audit-test-coverage-2026-07-25.md — test-coverage audit
- docs/audit-2026-04-07.md — first comprehensive audit
- docs/plans/ — design plans and decision records (each carries a verified status header)
- docs/contributing.md
- docs/security.md
Contributing
- Create a branch from
dev(the active development branch). - Keep changes focused and tested.
- Open a PR targeting
dev—devis merged tomainfor releases.
See docs/contributing.md for the full process.
License
AGPL-3.0


