Add Android client, packaging, Makefile entrypoint and handover docs

Why: the set has to be installable and publishable, not just buildable, and
the next person needs the reasoning, not only the code.

What:
- android/: Kotlin client with no third-party dependencies. A
  DocumentsProvider publishes the mirrored databases to every file picker, so
  KeePassDX opens them as ordinary documents; closing a file it wrote
  triggers a sync. A foreground service holds the event stream open.
  Mirror.decide transcribes the Go merge table, so both platforms resolve a
  conflict identically.
- Makefile as the single entrypoint, with file targets so only what changed
  is rebuilt: host and cross builds, both .deb packages, the macOS DMG with
  its /Applications symlink, the Windows zip, the APK under its F-Droid
  publishing name, and manual-only android-publish.
- versions.mk holds one version per platform plus the Android versionCode;
  `make bump` and `make changelog` move them. Debian changelog is tracked.
- systemd units (hardened system unit for the server, user unit for the
  agent), macOS bundle and launch agent, Windows Task Scheduler job.
- docs/HANDOVER.md on architecture and every decision that shaped it,
  docs/PROTOCOL.md for the API, README for the quick start.

Proved: make check and make test-race both pass; built both .deb packages and
inspected their contents, built the Windows zip, cross-compiled all five
desktop targets, and exercised make bump. The Android module is written but
not compiled: no JDK or Android SDK in this environment, which HANDOVER.md
states plainly.
This commit is contained in:
2026-10-29 16:26:33 +01:00
parent 4d0d53ff04
commit 0e83acb9b1
40 changed files with 2563 additions and 24 deletions
+11
View File
@@ -0,0 +1,11 @@
keeporg (0.1.0) unstable; urgency=medium
* First release.
* keeporgd: synchronization server with nested group access control,
invite-link and OIDC registration, built-in TOTP second factor,
versioned KDBX blobs and server-sent update notifications.
* keeporg: desktop client that mirrors assigned databases as ordinary
.kdbx files, pushes every save and applies remote changes, with
server-wins, client-wins and manual merge strategies.
-- tiredsysadmin <rcmd@garage.rcmd.space> Thu, 29 Oct 2026 12:00:00 +0000
+17
View File
@@ -0,0 +1,17 @@
Package: keeporg
Version: @VERSION@
Section: utils
Priority: optional
Architecture: @ARCH@
Maintainer: tiredsysadmin <rcmd@garage.rcmd.space>
Homepage: https://garage.rcmd.space/keeporg
Description: KeePass database synchronization client
KeepOrg mirrors the KDBX databases a KeepOrg server has assigned to you into
one directory as ordinary files, so KeePassXC, KeePass2 or any compatible
application opens them directly.
.
Each save is pushed to the server and changes other people push are applied
locally. The client never opens a database and never sees a master password.
.
A systemd user unit is included: enable it with
`systemctl --user enable --now keeporg`.
+14
View File
@@ -0,0 +1,14 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: keeporg
Source: https://garage.rcmd.space/keeporg
Files: *
Copyright: 2026 tiredsysadmin
License: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
.
On Debian systems the full text is in
/usr/share/common-licenses/AGPL-3.
+19
View File
@@ -0,0 +1,19 @@
Package: keeporgd
Version: @VERSION@
Section: net
Priority: optional
Architecture: @ARCH@
Maintainer: tiredsysadmin <rcmd@garage.rcmd.space>
Homepage: https://garage.rcmd.space/keeporg
Depends: adduser
Description: KeePass database synchronization server
KeepOrg stores KDBX files as opaque versioned blobs and arbitrates concurrent
writes between clients. It hosts many databases, controls access through a
tree of user groups, and accepts registrations by invite link or OIDC.
.
It never opens a database and cannot read one: master passwords stay on the
clients.
.
The service runs as the keeporg user with its data in /var/lib/keeporg and
its configuration in /etc/keeporg/server.json. Terminate TLS in front of it
with nginx or caddy, or point tls_cert and tls_key at a certificate.
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>KeepOrg</string>
<key>CFBundleDisplayName</key>
<string>KeepOrg</string>
<key>CFBundleIdentifier</key>
<string>@APPID@</string>
<key>CFBundleVersion</key>
<string>@VERSION@</string>
<key>CFBundleShortVersionString</key>
<string>@VERSION@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleExecutable</key>
<string>KeepOrg</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<!-- The agent has no window: it syncs in the background. -->
<key>LSUIElement</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- Copy to ~/Library/LaunchAgents/@APPID@.plist and run
launchctl load ~/Library/LaunchAgents/@APPID@.plist
to keep the agent running after login. -->
<plist version="1.0">
<dict>
<key>Label</key>
<string>@APPID@</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/KeepOrg.app/Contents/MacOS/keeporg</string>
<string>run</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/keeporg.log</string>
</dict>
</plist>
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
# Bundle executable: starts the synchronization agent. If this device has not
# been signed in yet, open Terminal so the user can do it.
set -eu
here=$(cd "$(dirname "$0")" && pwd)
keeporg="$here/keeporg"
config="$HOME/Library/Application Support/keeporg/config.json"
if [ ! -f "$config" ]; then
osascript -e "tell application \"Terminal\" to do script \"'$keeporg' login -server https://keeporg.example.com -user \$USER\"" \
-e 'tell application "Terminal" to activate'
exit 0
fi
exec "$keeporg" run
+16
View File
@@ -0,0 +1,16 @@
[Unit]
Description=KeepOrg KDBX synchronization agent
Documentation=https://garage.rcmd.space/keeporg
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/keeporg run
Restart=on-failure
RestartSec=10
# The agent only ever touches the mirror directory and the config file.
NoNewPrivileges=yes
PrivateTmp=yes
[Install]
WantedBy=default.target
+38
View File
@@ -0,0 +1,38 @@
[Unit]
Description=KeepOrg KDBX synchronization server
Documentation=https://garage.rcmd.space/keeporg
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=keeporg
Group=keeporg
ExecStart=/usr/bin/keeporgd -config /etc/keeporg/server.json
Restart=on-failure
RestartSec=5
StateDirectory=keeporg
StateDirectoryMode=0700
ConfigurationDirectory=keeporg
# The server reads one config file and writes one data directory.
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ReadWritePaths=/var/lib/keeporg
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
+30
View File
@@ -0,0 +1,30 @@
KeepOrg for Windows
===================
keeporg.exe is the synchronization agent and the administration command.
First run
---------
keeporg.exe login -server https://keeporg.example.com -user you
keeporg.exe run
`login` stores its configuration in
%AppData%\keeporg\config.json and mirrors your databases into
%UserProfile%\KeepOrg by default. Pass -dir to choose another directory.
Open the .kdbx files in that directory with KeePass, KeePassXC or any
compatible application. Every save is pushed to the server, and changes made
by other people are applied to the files.
Keeping it running
------------------
Import keeporg-task.xml into Task Scheduler to start the agent at logon:
schtasks /create /tn KeepOrg /xml keeporg-task.xml
Conflicts
---------
If someone else saved first, your save is kept beside the file as
<name>.conflict-<timestamp>.kdbx and the server copy is restored. Open both
in KeePass and merge them, or ask a group administrator to switch the
database to another merge strategy.
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>KeepOrg KDBX synchronization agent</Description>
<URI>\KeepOrg</URI>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<RestartOnFailure>
<Interval>PT1M</Interval>
<Count>3</Count>
</RestartOnFailure>
</Settings>
<Actions Context="Author">
<Exec>
<Command>%LOCALAPPDATA%\Programs\KeepOrg\keeporg.exe</Command>
<Arguments>run -quiet</Arguments>
</Exec>
</Actions>
</Task>