Repository navigation
API Reference
Base URL: http://localhost:8080
- Parameters marked Yes are required; No means optional (default noted).
- Parameters are sent as multipart form fields (
curl -F), never in the query string — secrets stay out of URLs, access logs and proxies. On the wire they are still plaintext until HTTPS is enabled. -
Authorizationis a request header whose value is the raw token returned by/login— noBearerprefix. -
-F 'name=value'for direct values,-F 'field=<-'to stream a file or pipe (gpg output) into a field.
Quick flow
TOKEN=$(curl -s -F 'name=alice' -F 'password=secret' "http://localhost:8080/login")
curl -s -H "Authorization: $TOKEN" "http://localhost:8080/groups"Returns all users. Serialized with the Public view: only name and pgp (no password hash, no tokens).
curl "http://localhost:8080/users"Response: [{"name":"alice","pgp":null}]
Returns one user (public view) or null if the name does not exist.
| Parameter | In | Required |
|---|---|---|
name |
path | Yes |
curl "http://localhost:8080/user/alice"Creates a new user. The password is stored as SHA-512.
| Parameter | In | Required |
|---|---|---|
name |
form | Yes |
password |
form | Yes |
curl -F 'name=alice' -F 'password=secret' "http://localhost:8080/user"- If the name is already in use, the response body is
nulland nothing is created.
Updates the authenticated user (name, password and/or PGP key). Nothing is cleared unless you ask for it.
| Parameter | In | Required | Default |
|---|---|---|---|
Authorization |
header | Yes | — |
name |
form | No | unchanged |
password |
form | No | unchanged |
pgp |
form | No | unchanged |
pgp_null |
form | No |
false (set true to delete the stored PGP key) |
# change the password
curl -X PUT -H "Authorization: $TOKEN" -F 'password=newsecret' "http://localhost:8080/user"
# export the PGP public key from the keyring and pipe it into the field
gpg --export --armor <id-pgp> \
| curl -X PUT -H "Authorization: $TOKEN" -F 'pgp=<-' "http://localhost:8080/user"
# remove the PGP key
curl -X PUT -H "Authorization: $TOKEN" -F 'pgp_null=true' "http://localhost:8080/user"Deletes the authenticated user.
| Parameter | In | Required |
|---|---|---|
Authorization |
header | Yes |
curl -X DELETE -H "Authorization: $TOKEN" "http://localhost:8080/user"Warning
The user's entries in group userRole maps are not cleaned up; groups keep a stale name reference.
Returns all tokens of the authenticated user (sessions).
| Parameter | In | Required |
|---|---|---|
Authorization |
header | Yes |
curl -H "Authorization: $TOKEN" "http://localhost:8080/tokens"Exchanges name + password for a token (32 random bytes, Base64url).
| Parameter | In | Required |
|---|---|---|
name |
form | Yes |
password |
form | Yes |
curl -F 'name=alice' -F 'password=secret' "http://localhost:8080/login"
# → NnQ8v... (raw token string)- Unknown user →
404 Not Found. - Wrong password → returns the string
ainstead of an error (see Home#Known limitations). - The password is not PGP-encrypted: the server verifies it as the SHA-512 hash of the plaintext, so it cannot decrypt it. HTTPS is what protects it on the wire.
Deletes one of your tokens. The user is resolved through the token form field.
| Parameter | In | Required |
|---|---|---|
Authorization |
header | Yes |
token |
form | Yes (the token to revoke) |
curl -X DELETE -H "Authorization: $TOKEN" \
-F 'token=OTHER_TOKEN' "http://localhost:8080/token"Deletes the token used in the Authorization header (ends that session).
| Parameter | In | Required |
|---|---|---|
Authorization |
header | Yes |
curl -X DELETE -H "Authorization: $TOKEN" "http://localhost:8080/logout"Role = the caller's bit mask inside that group — see Roles.
Returns every group the authenticated user belongs to.
| Parameter | In | Required |
|---|---|---|
Authorization |
header | Yes |
curl -H "Authorization: $TOKEN" "http://localhost:8080/groups"Response: [{"id":0,"name":"team"}] (userRole map is also included).
Returns the group if the caller is a member, otherwise null.
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
Authorization |
header | Yes |
curl -H "Authorization: $TOKEN" "http://localhost:8080/group/0"Creates a group. The creator is stored with role 127 (full control).
| Parameter | In | Required |
|---|---|---|
name |
form | Yes |
Authorization |
header | Yes |
curl -F 'name=team' -H "Authorization: $TOKEN" "http://localhost:8080/group"Response includes the assigned id (first group is 0).
Adds an existing user to the group. Requires bit 2 (add user).
| Parameter | In | Required | Default |
|---|---|---|---|
id |
path | Yes | — |
name |
form | Yes (user to add) | — |
roles |
form | No |
3 (read + write) |
Authorization |
header | Yes | — |
# add bob with default role 3
curl -H "Authorization: $TOKEN" -F 'name=bob' "http://localhost:8080/group/0"
# add bob as moderator (read+write+add+delete user = 1+2+4+16 = 23)
curl -H "Authorization: $TOKEN" -F 'name=bob' -F 'roles=23' "http://localhost:8080/group/0"- Unknown target user or missing permission →
nullbody, nothing changes.
Requires bit 5 (update group).
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
name |
form | Yes (new group name) |
Authorization |
header | Yes |
curl -X PUT -H "Authorization: $TOKEN" \
-F 'name=new-team-name' "http://localhost:8080/group/0"Overwrites the role of the member {name}. Requires bit 3 (update roles) on the caller; both users must be in the group.
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
name |
path | Yes (member whose role changes) |
roles |
form | Yes (Byte, 0–127) |
Authorization |
header | Yes |
# promote bob to 127 (admin)
curl -X PUT -H "Authorization: $TOKEN" \
-F 'roles=127' "http://localhost:8080/group/0/bob"Requires bit 6 (delete group).
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
Authorization |
header | Yes |
curl -X DELETE -H "Authorization: $TOKEN" "http://localhost:8080/group/0"Note
The group's message file data/groups/<id>.json is not deleted.
Removes the caller from the group. Membership only, no bit needed.
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
Authorization |
header | Yes |
curl -X DELETE -H "Authorization: $TOKEN" "http://localhost:8080/exit/0"- The last member (typically role 127) can leave, orphaning the group.
Removes {name} from the group. Requires bit 4 (delete user).
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
name |
path | Yes (member to remove) |
Authorization |
header | Yes |
curl -X DELETE -H "Authorization: $TOKEN" "http://localhost:8080/group/0/bob"Messages live in data/groups/<id>.json. The server never encrypts — you encrypt client-side with GnuPG and the server stores only the armored ciphertext.
Returns all messages of the group. Requires bit 0 (read).
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
Authorization |
header | Yes |
curl -H "Authorization: $TOKEN" "http://localhost:8080/messages/0"Response: [{"content":"-----BEGIN PGP MESSAGE-----...","date_post":"2026-10-07T12:00:00"}]
Appends a message. Requires bit 1 (write). The content must contain one of:
-----BEGIN PGP PUBLIC KEY BLOCK----------BEGIN PGP PRIVATE KEY BLOCK----------BEGIN PGP MESSAGE----------END PGP MESSAGE-----
| Parameter | In | Required |
|---|---|---|
id |
path | Yes |
message |
form | Yes |
Authorization |
header | Yes |
Encrypt to the recipient's public key and stream the ciphertext straight into the form field:
gpg --encrypt --armor --recipient <id-pgp> message.txt \
| curl -H "Authorization: $TOKEN" -F 'message=<-' "http://localhost:8080/send/0"Short flags — gpg -ear <id-pgp>:
gpg -ear <id-pgp> message.txt \
| curl -H "Authorization: $TOKEN" -F 'message=<-' "http://localhost:8080/send/0"- Encrypt once per recipient key for private conversations, or to a shared group key.
- Content without a PGP marker is silently discarded (HTTP 200, nothing stored).
- Response body is empty on success.
| Code | When |
|---|---|
200 |
Success (also returned with a null body when a permission check fails or an entity is missing) |
400 |
Missing required parameter |
404 |
/login with unknown user |
Important
Permission failures do not return 403 — the endpoints respond with an empty/null body. Treat null as "not allowed or not found".