- Dart 54%
- C++ 14%
- Python 10.9%
- CMake 10.5%
- Rust 8.7%
- Other 1.8%
|
Some checks are pending
Desktop builds / build (desktop/build/linux/x64/release/bundle, ubuntu-24.04, linux) (push) Waiting to run
Desktop builds / build (desktop/build/macos/Build/Products/Release/*.app, macos-latest, macos) (push) Waiting to run
Desktop builds / build (desktop/build/windows/x64/runner/Release, windows-latest, windows) (push) Waiting to run
Server and deployment tests / tests (push) Waiting to run
|
||
|---|---|---|
| .github/workflows | ||
| config | ||
| desktop | ||
| scripts | ||
| server | ||
| .dockerignore | ||
| .gitignore | ||
| compose.yaml | ||
| Dockerfile | ||
| README.md | ||
Project Commander
Rust metadata sync server and Flutter tray desktop client for macOS, Linux and Windows. The client supports GitHub, Gitea, Forgejo and Codeberg links, SSH/HTTPS Git URLs, and automatic discovery of local and linked repositories.
End-to-end encrypted vault
The server stores an opaque client-encrypted vault. It has no vault key, recovery secret, encryption password, or decrypt operation. Repository URLs, GitHub repository tokens, settings, and any recovery material belong inside that encrypted payload. No project files are stored.
OAuth authenticates access to storage only; it cannot unlock a vault. A native client must generate a random 256-bit vault key locally, store it in platform secure storage, and use XChaCha20-Poly1305 with a fresh random 24-byte nonce on every write. Use the stable vault_id returned at login as part of authenticated additional data: UTF-8 pcs:v1:<vault_id>. Clients must verify tags before using any decrypted content. The server validates envelope format, not cryptographic authenticity; it cannot prove that a client encrypted its input correctly.
New-device enrollment requires an existing trusted device or an independently transferred recovery key. Never send the key to the server, derive it from OAuth tokens, or trust a server-supplied replacement key. The desktop supports explicit recovery-key export and import; automated trusted-device pairing remains future work. Losing all key copies means losing access; administrators cannot recover the vault. Device removal requires rotating the vault key and sharing it only with retained devices.
The administrator still sees OAuth identity, IP addresses, request timing, ciphertext size and revisions, and can delete or replay stored data. Native clients must retain revision/checkpoint history to detect rollback; encryption alone does not prevent rollback. A compromised client or malicious client update can expose keys. This server change establishes the opaque-storage boundary, not a completed audited end-to-end system.
Authentication and GitHub
Configure GitHub OAuth with identity-only read:user scope. Login access tokens are transiently used for identity lookup and never persisted or returned. The administrator can observe these authentication tokens, so they must not carry repository permissions.
Repository access must use a separate device-side GitHub authorization flow. Repository tokens must never traverse this server as plaintext; clients contact GitHub directly for import, cloning credentials and optional SSH/GPG public-key management. Private keys remain local. Generic OAuth providers such as Authentik are supported simultaneously through configuration. Identity/token exchange remains server-side and is outside vault confidentiality.
API
GET /api/auth/providers lists configured providers, base64 PNG icons, and the optional public github_device_client_id for direct desktop GitHub authorization. GET /auth/{id}?handoff_challenge= starts OAuth. The callback returns completion status only. POST /api/auth/exchange with {"handoff_secret":"<base64url random 32 bytes>"} returns {"pending":true} until completion, then consumes a one-time session JSON containing expiry and vault_id. Handoff results expire after ten minutes.
Authenticated GET /api/sync returns {"revision":0,"envelope":null} for a new vault. PUT requires {"expected_revision":0,"envelope":{"version":1,"algorithm":"XChaCha20-Poly1305","nonce":"<standard base64, 24 bytes>","ciphertext":"<standard base64 ciphertext including 16-byte tag>"}}. Bearer session authentication is required. Unknown envelope fields and plaintext repo-list requests are rejected. Ciphertext is capped at 1 MiB. Revision conflicts return 409; writes are serialized within one server process. Run one replica per data volume.
Interactive deployment
Run python3 scripts/deploy.py (Python 3.10+), or sh scripts/deploy.sh. Choose Docker or Kubernetes. The wizard prints credential URLs exactly when needed, collects secrets without echo, and writes a new private deployment directory without overwriting existing files. Use --generate-only --output /path/to/new-directory to prepare files without deploying.
Two separate GitHub OAuth applications are used: an identity-only server app with callback <public HTTPS URL>/auth/callback, and a desktop app with Device Flow enabled. Only the desktop app's public client ID is announced to clients; its secret is never needed. The wizard also supports simultaneous generic OAuth providers, such as Authentik. Custom provider icons must be 64×64 PNGs; a neutral placeholder is available.
Docker output includes Compose JSON and Caddy automatic HTTPS. Point DNS at the host and open ports 80/443. The server port is internal; configuration and encrypted-vault data use private bind directories. Running as root prepares those bind directories for UID/GID 10001; otherwise the server uses the invoking UID/GID. For a remote Docker daemon, generate/run the deployment on that host so bind paths resolve correctly.
Kubernetes output includes an OAuth configuration Secret, optional GHCR pull Secret, PVC, one-replica Deployment, Service and TLS Ingress. Supply an existing ingress class, working storage class and TLS secret/certificate controller. The wizard uses an explicitly selected kubectl context. It does not install an ingress controller, provision DNS or issue Kubernetes TLS certificates. Kubernetes Secrets contain authentication credentials; configure cluster RBAC/encryption-at-rest as appropriate. They never contain vault encryption keys.
Private GHCR pulls need a classic PAT with read:packages and package/organization access; the wizard supplies the GitHub creation URL. Docker login receives it via stdin; Kubernetes embeds it in an image-pull Secret. Generated files containing credentials are mode 0600 and their directory is ignored by Git. Back up persistent encrypted-vault data and keep users' recovery keys separately. Do not run multiple server replicas against one data volume.
For manual local builds, config/server.example.json documents the server configuration. Mount config/ read-only and a persistent data/ directory. No PCS_ENCRYPTION_KEY is used. Server sessions expire after 24 hours and are invalidated by a restart.
Validation and distribution
Server unit tests, deployment generation tests, desktop unit tests and native desktop builds run in GitHub Actions. GHCR publishes amd64 and arm64 images tagged latest and sha-. Desktop builds are unsigned; production distribution needs platform signing/notarization. Full live OAuth and infrastructure verification require real deployment credentials.
The server remains a small single-process implementation: session administration/revocation, production rate limiting and an independent security audit remain future hardening work. Authentication metadata is visible to the server; vault plaintext and keys are device-owned.