64d2e1087b chore: merge main into dev to unblock the alpha.4 release PR (#1425)
* ci(deps): bump anthropics/claude-code-action (#1404)

Bumps the actions-dependencies group with 1 update: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action).


Updates `anthropics/claude-code-action` from 1.0.193 to 1.0.199
- [Release notes](https://github.com/anthropics/claude-code-action/releases)
- [Commits](https://github.com/anthropics/claude-code-action/compare/9d7150bc8a3dae8149739a88019d192b579ad90c...dcb57747bfceeaa1fa72638cae52295d1d853d4a)

---
updated-dependencies:
- dependency-name: anthropics/claude-code-action
  dependency-version: 1.0.199
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): pin rfd to tauri-plugin-dialog's major to unblock the cargo group (#1406)

The cargo-dependencies group PR (#1405) fails Rust Unit Tests on Linux:

    error: failed to run custom build command for `rfd v0.17.2`
    You need to choose at least one backend: `gtk3` or `xdg-portal`
    features for x86_64-linux

rfd is not really ours. It arrives in the tree via tauri-plugin-dialog,
which pins ^0.16; we declare it directly only for the fatal-startup
message box in lib.rs, where the Tauri app never finished building and
the plugin has no AppHandle to run a dialog through.

Cargo unifies features only within a semver-compatible version group, so
while both wanted ^0.16 there was a single rfd in the graph and the
plugin's backend features covered our `default-features = false`
declaration too. Bumping our direct dep to 0.17 forks rfd into two
crates: the plugin keeps 0.16.0 with its features, ours resolves to
0.17.2 with none, and rfd 0.17 added a build.rs assertion that aborts
the Linux build when no backend feature is set. Confirmed in the PR's
lockfile, which carries both 0.16.0 and 0.17.2.

Adding a Linux backend feature would be the wrong fix: it would paper
over the fork and still build rfd twice on every platform for one error
dialog. Our version has to track the plugin's instead, so ignore
semver-minor rfd updates (0.16 -> 0.17 for a 0.x crate) until
tauri-plugin-dialog moves. Patch updates inside 0.16.x still flow.

The remaining five crates in the group are unaffected; `windows` in fact
consolidates 3 versions down to 2.

Cargo.toml is comment-only here - no dependency, feature, or lockfile
change - so the build is untouched.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(deps): bump log (#1407)

Bumps the cargo-dependencies group with 1 update in the /Client/tauri-client/src-tauri directory: [log](https://github.com/rust-lang/log).


Updates `log` from 0.4.33 to 0.4.34
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.33...0.4.34)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.34
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci(deps): bump anthropics/claude-code-action (#1408)

Bumps the actions-dependencies group with 1 update: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action).


Updates `anthropics/claude-code-action` from 1.0.199 to 1.0.200
- [Release notes](https://github.com/anthropics/claude-code-action/releases)
- [Commits](https://github.com/anthropics/claude-code-action/compare/dcb57747bfceeaa1fa72638cae52295d1d853d4a...24dcd50c0568f0fc9e9211213a4fd2d9eb15c4e0)

---
updated-dependencies:
- dependency-name: anthropics/claude-code-action
  dependency-version: 1.0.200
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(client): strip tags to a fixpoint inside sanitizePassApprox

CodeQL alert 17 (js/incomplete-multi-character-sanitization, high) fires on
the single-pass `input.replace(/<[^>]*>/g, "")`: a lone replace can in
principle splice a fresh `<...>` out of the text either side of what it
removed. echoNormalize already loops sanitizePassApprox to a fixpoint, so
that was absorbed one level up and the output is unchanged -- but the
repetition is now where a reader (and the query) can see it.

sanitizePassApprox is a comparison normalizer, never rendered output: its
only consumer is the `===` echo match in isUnreconciledEcho. Not a
sanitization boundary, so this is a legibility fix, not a security one.

Client suite 5257/5257, tsc, lint, hygiene all green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(client): put the strip-tags replace inside the loop body

The previous form hoisted the `replace` into the `for` header's init
expression, so CodeQL still reported it (alert 18, line 217 col 21) --
js/incomplete-multi-character-sanitization only credits a repeated
replacement when the call sits in the loop *body*, which is also the shape
the rule's own guidance shows.

Same fixpoint, same output; `while (out.includes("<"))` gives the loop a
real condition instead of `for (;;)`.

Client suite 5257/5257, tsc, lint, prettier green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 20:12:52 +00:00
2026-08-27 21:37:40 +02:00
2026-08-27 21:37:40 +02:00
2026-08-27 21:37:40 +02:00

CI Release Status Go Tauri Platforms License: AGPL-3.0

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, it comes with no support commitment, 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.

OwnCord Client

Login Page Admin Panel

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

Quick Start

Option A: Prebuilt binaries

  1. Download assets from Releases (binaries, checksums, signatures, and a full source snapshot per release).
  2. Run the server binary:
    • Windows: chatserver.exe
    • Linux: ./chatserver
  3. Open https://localhost:8443/admin and complete the setup wizard — it creates your Owner account and configures the server for you (settings are saved to config.yaml automatically).
  4. 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.enabled and 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 24+ (see Client/.nvmrc)
  • 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.4" .

# Server (Linux)
cd Server
CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.4" .

# Client
cd Client
npm install
npm run tauri build

Core verification commands

Everything CI gates on, from the repository root:

npm run check                  # server + client + Rust
npm run check:server           # or one stack at a time
node scripts/run.mjs --list    # exactly what each task runs, and where

Or run the stacks directly — the facade is a convenience, not the only path, and server work needs no Node at all:

# Server
cd Server
go test ./...

# Client
cd 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_KEY
  • TAURI_SIGNING_PRIVATE_KEY_PASSWORD
  • SERVER_UPDATE_SIGNING_PRIVATE_KEY
  • SERVER_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/README.md is the complete index — every document in docs/, grouped by whether it is guidance, a reference contract, a dated audit, or a plan. The most-used entries:

Audits are dated snapshots and are not maintained after the fact — read them as history. docs/README.md lists all nine, newest first.

Contributing

  1. Create a branch from dev (the active development branch).
  2. Keep changes focused and tested.
  3. Open a PR targeting devdev is merged to main for releases.

See docs/contributing.md for the full process.

Getting Help and Reporting Problems

Nothing here is a support promise — see the note at the top — but there is a right place for each kind of message:

Kind Where
A reproducible bug Issues
A question about setup or usage Discussions → Q&A
An idea or feature suggestion Discussions → Ideas
A security vulnerability Private advisory — never an issue

License

AGPL-3.0

S
Description
OwnCord is a self-hosted, open-source chat platform with text channels, voice/video chat, direct messages, and a desktop client — built for communities that want full control over their data.
Readme AGPL-3.0
111 MiB
Languages
TypeScript 48.7%
Go 44%
JavaScript 2.2%
Rust 2.1%
CSS 1.4%
Other 1.5%