Library / SDK
Sharkord/sharkord avatar
Sharkord/sharkord

Sharkord bundles a voice server and its web client into one binary, then prints the owner token to your console

Lightweight, self-hosted, open-source chat server with voice, video, text, and screen sharing. Built for small groups who want privacy, simplicity, and full control over their data.

1,530 stars143 forksTypeScriptMIT

At a glance

What is it?
Sharkord is a self-hosted TypeScript chat server on Bun, tRPC, mediasoup and Drizzle, distributed as a single binary for Windows, macOS and Linux. It is declared alpha, its default branch is development, and the credential that owns the server is printed on first launch and also signs every session and file URL.
Who is it for?
Sharkord is worth trying if you want a small-group server you control and you can live with pre-1.0 churn, since breaking changes are stated up front. Read the token warning before the first run rather than after, decide how you will protect a credential that is printed to stdout and doubles as a signing key, and pin a release version instead of tracking latest in any script you automate.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The first launch prints the credential that owns the server

The warning block under the Docker instructions is the most important paragraph in the file, and it is easy to skim past. On first launch Sharkord creates a secret token and prints it to the console. That single value is two things at once: it is the credential that grants owner access, and it is the key the server signs every session and file URL with. The consequence is stated without hedging, that anyone who obtains it can take ownership and impersonate any account.

So the privilege model collapses into one string. Owner rights are not a role you can grant and revoke, because owner is whoever holds the token. Session signing and file URL signing ride on the same secret, which connects it to the optional signed URLs in the file upload feature, where per-user storage quotas and signed access are described together. The instruction that follows is practical: keep it out of logs, screenshots and issue reports, store it securely, and do not lose it.

What the file does not add is a rotation procedure. Nothing describes a command to reissue the token, an audit trail of when it was last used, or a second tier of credential for day to day administration. The roles and permissions system, with per-channel overrides for individual roles and users, and invites with usage limits and automatic role assignment, all sit underneath that one root secret. For an alpha project that is a coherent choice, but it is a choice worth making deliberately.

One binary serves server and client, and only one install line is written down

Sharkord is distributed as a standalone binary that bundles both the server and the client, and binaries are shipped for Windows, macOS and Linux. What is documented is the Linux x64 path, three commands long:

bash
curl -L https://github.com/sharkord/sharkord/releases/latest/download/sharkord-linux-x64 -o sharkord
chmod +x sharkord
./sharkord

Two details in those lines are worth noting. The path uses a lowercase owner and repository where the canonical one is capitalised, which works because the host resolves case insensitively but does not survive being pasted into a script that checks names. More usefully, `latest/download` follows the newest tag at the moment you run it, so there is no version in the command and nothing in the file tells you what you are about to get.

For Windows and macOS the file names the platforms and stops, with no command, no checksum and no note about what to do with the file once downloaded. The one documented port is the client, http://localhost:4991, with the note that a server on another machine means substituting its IP address or domain. Configuration, setup options and troubleshooting are all deferred to the documentation site rather than the repository, so the practical reference for anything beyond start up lives outside the tree.

Docker opens port 40000 on both tcp and udp, and the image is tagged latest

The container path is a single run command:

bash
docker run \
  -p 4991:4991/tcp \
  -p 40000:40000/tcp \
  -p 40000:40000/udp \
  -v ./data:/home/bun/.config/sharkord \
  --name sharkord \
  sharkord/sharkord:latest

Three port mappings, two of them for the same port number on different protocols, which is the mediasoup SFU asking for the bandwidth it needs and also telling you what a firewall has to allow. Nothing in the text names which service owns 40000 or 4991, so the mapping has to be taken on trust from the run line. The volume mounts a host directory into the container user's config directory, which is where the token and state end up, and no environment variable is passed to override that path.

The tag is `latest`, again with no digest and no version. That matters more here than in a typical application image, because the container also carries the credential handling described above: a data directory on the host holds the secret, and pulling a different image over it is how you change the code that reads it. A deployment that copies this line verbatim and pulls weekly can therefore change versions without anyone deciding to. Pin a tag, or better a digest, before this reaches a machine that stays up.

The image copies prebuilt binaries it never builds

The Dockerfile starts from oven/bun:1.3.14 and immediately switches to root with USER root, which it never switches back. Then it copies two files out of the build tree rather than compiling anything:

dockerfile
COPY apps/server/build/out/sharkord-linux-x64 /tmp/sharkord-linux-x64
COPY apps/server/build/out/sharkord-linux-arm64 /tmp/sharkord-linux-arm64

A case statement on the TARGETARCH build argument picks one of them, copies it to `/sharkord`, marks it executable, changes its owner to bun, and deletes the originals. Any other architecture exits with an unsupported arch message. The rest creates and chowns `/home/bun/.config/sharkord`, copies docker-entrypoint.sh to `/entrypoint.sh`, sets the working directory to `/home/bun` and points ENTRYPOINT at that script. An environment flag RUNNING_IN_DOCKER is set so the application can tell it is containerised.

The consequence is that the image is a wrapper, not a builder. The `build` keyword next to each service in the compose-oriented instructions has nothing to compile here, because the Bun cross-compiler output under apps/server/build/out has to exist on the host before docker build runs. It also means the runtime base carries a full Bun toolchain and a root-owned entrypoint for what is ultimately one static binary, and that the arm64 artefact exists even though the documented download line covers only linux-x64.

The root package declares no dependencies and no Bun version

package.json at the repository root is a workspace definition and a task runner, not a dependency manifest. Its name is sharkord, its version is 0.0.25, and `private` is true, so the root itself is never published. Everything else lives under the apps and packages globs. The only devDependencies are @types/bun at 1.3.14 and knip at ^5.80.0, and the overrides block pins @types/bun to the same 1.3.14, which is how the type definitions are held still while everything else floats.

Two scripts are worth naming. `magic` chains format, check-types and lint into one command, which tells you the intended pre-commit gate. `knip` and `knip:production` are separate, and knip.json at the root configures a tool whose entire job is finding exports nothing imports, so unused surface is a tracked problem rather than an accident.

`trustedDependencies` contains exactly one entry, mediasoup. Bun blocks lifecycle scripts for packages it does not trust, and an SFU that binds sockets needs its native build to run, so this is the dependency the project had to make an exception for. That is also the dependency behind the port 40000 mapping, which ties the audio path to the build path.

One gap: the Dockerfile pins the Bun base image to 1.3.14 while package.json has no engines field at all, so nothing tells a contributor which Bun version the project expects outside the container build.

The screenshots heading has nothing under it and the badges have no names

The section order is What is Sharkord, then Screenshots, then Features, and the Screenshots heading is followed immediately by the Features heading. There are no images between them, in a project whose pitch is voice and video and whose README opens with five badge links. Those five links are also empty markdown links with no alt text, and two of them point at the same releases page, so the row of badges renders as blank squares leading to the same place three times.

The feature list itself is broad and flat: voice channels with video and screen sharing over the built-in mediasoup SFU, text channels grouped into categories with threads, replies, reactions, pins and search, direct messages, roles and permissions with per-channel overrides for individual roles and users, custom emoji, mentions and channel references, invites with usage limits and automatic role assignment, file uploads with per-user storage quotas and optional signed URLs, and plugins that extend both server and client.

Set that against the note at the top, which says Sharkord is in alpha stage and that bugs, incomplete features and breaking changes are to be expected. The list is a target, not a status report, and the extension point is named concretely: a plugin SDK at packages/plugin-sdk, documented as reaching both sides of the system. What the file never offers is a per-feature status, so there is no way from the README alone to learn which of those eight items is finished.

Version 0.0.25, releases weeks apart, default branch development

The version scheme is pre-1.0 and the releases move at a slow, irregular pace. The three most recent tags are v0.0.23 on 2026-07-10, v0.0.24 on 2026-08-27 and v0.0.25 on 2026-09-04, and package.json still reads 0.0.25. The last push is dated 2026-09-11, seven days after the newest tag, so the tip of the default branch has already moved past the released version. Nothing is archived, and the cadence is that of an alpha project rather than a stalled one.

The branch name is worth knowing before you clone. The default branch is development, not main, so a plain clone gets unreleased work, and the `releases/latest/download` path in the install line points at the newest tag instead, which is a different piece of code from the one you just cloned. Anyone comparing the two will find differences that are not bugs.

The rest of the root tells you where the work lives. AGENTS.md and CLAUDE.md sit side by side for agent instructions, CONTRIBUTING.md and DEVELOPMENT.md are the guides the README points at, ROADMAP.md carries intent, and flake.nix with flake.lock and .envrc put a Nix and direnv toolchain in place for local development alongside bun.lock. Two loose shell scripts, start.sh and clear_node_modules.sh, handle the chores that a workspace of this size accumulates. The live demo at demo.sharkord.com and the documentation at sharkord.com/docs are the paths the README prefers over documenting anything itself.

Editorial conclusion

Sharkord is worth trying if you want a small-group server you control and you can live with pre-1.0 churn, since breaking changes are stated up front. Read the token warning before the first run rather than after, decide how you will protect a credential that is printed to stdout and doubles as a signing key, and pin a release version instead of tracking latest in any script you automate.

Frequently asked questions

sharkord vs stoat

This repository makes no comparison with stoat or with any other server. What it states is its own scope: a self-hosted TypeScript platform with voice, video and screen sharing over a built-in mediasoup SFU, text channels with threads and search, roles with per-channel overrides, invites with usage limits, file uploads with per-user quotas, and a plugin SDK.

sharkord vs matrix

There is no Matrix comparison here. The feature set Sharkord describes is voice and video channels, categorised text channels with threads, replies, reactions, pins and search, direct messages, roles and permissions, custom emoji, invites with limits, file uploads, and plugins for server and client.

How do I install Sharkord on Linux?

Download the standalone binary that bundles server and client, make it executable and run it, then open http://localhost:4991. Docker is the alternative, mapping port 4991 on tcp and port 40000 on both tcp and udp with the data directory mounted at /home/bun/.config/sharkord.

What is the secret token Sharkord prints on first launch?

It grants owner access and is the key the server signs every session and file URL with, so anyone who obtains it can take ownership and impersonate any account. The file says to keep it out of logs, screenshots and issue reports, store it securely, and not lose it.

Is Sharkord ready for everyday use?

It is declared alpha, with bugs, incomplete features and breaking changes expected. The version is 0.0.25 and the last push is dated 2026-09-11, seven days after the newest tag v0.0.25.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. Sharkord/sharkord on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sharkord-sharkord.svg)](https://hysenlabs.com/projects/sharkord-sharkord)