Skip to content

Commit 1c9d4a4

Browse files
committed
inbox: add health endpoint && docs
1 parent ab9efae commit 1c9d4a4

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

Notesnook.Inbox.API/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Notesnook Inbox API
2+
3+
## Running locally
4+
5+
### Requirements
6+
7+
- Bun (v1.0.0 or higher)
8+
9+
### Environment variables
10+
11+
- `PORT`
12+
- `NOTESNOOK_API_SERVER_URL`
13+
14+
### Commands
15+
16+
- `bun install` - Install dependencies
17+
- `bun run dev` - Start the development server
18+
- `bun run build` - Build the project for production
19+
- `bun run start` - Start the production server
20+
21+
## Self-hosting
22+
23+
...
24+
25+
## Writing from scratch
26+
27+
The inbox API server is pretty simple to write from scratch in any programming language and/or framework. There's only one endpoint that needs to be implemented, which does these three steps:
28+
29+
1. Fetch the user's public inbox API key from the Notesnook API.
30+
2. Encrypt the payload (via libsodium).
31+
3. Post the encrypted payload to the Notesnook API.
32+
33+
You can refer to the [source code](./src/index.ts) for implementation details.

Notesnook.Inbox.API/src/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ if (!NOTESNOOK_API_SERVER_URL) {
99
}
1010

1111
let sodium: typeof _sodium;
12+
let isReady = false;
1213

1314
const RawInboxItemSchema = z.object({
1415
title: z.string().min(1, "Title is required"),
@@ -39,6 +40,20 @@ interface EncryptedInboxItem {
3940
salt: string;
4041
}
4142

43+
/**
44+
* Encrypts raw data using a hybrid encryption scheme combining symmetric and asymmetric cryptography.
45+
*
46+
* The encryption process follows these steps:
47+
* - Generate a random symmetric password using XChaCha20-Poly1305-IETF key generation
48+
* - Generate a random salt for key derivation
49+
* - Derive an encryption key from the password using Argon2i13
50+
* - Generate a random nonce (IV) for the symmetric encryption
51+
* - Encrypt the data using XChaCha20-Poly1305-IETF with the derived key
52+
* - Encrypt the derived key using the recipient's public key (X25519 sealed box)
53+
*
54+
* @param {string} rawData - The plaintext data to encrypt
55+
* @param {string} publicKey - The recipient's X25519 public key encoded in base64 URL-safe format without padding.
56+
*/
4257
function encrypt(rawData: string, publicKey: string): EncryptedInboxItem {
4358
try {
4459
const password = sodium.crypto_aead_xchacha20poly1305_ietf_keygen();
@@ -133,6 +148,12 @@ app.use(
133148
limit: 60,
134149
})
135150
);
151+
app.get("/health", (_, res) => {
152+
if (!isReady) {
153+
return res.status(503).json({ status: "unavailable" });
154+
}
155+
return res.status(200).json({ status: "ok" });
156+
});
136157
app.post("/inbox", async (req, res) => {
137158
try {
138159
const apiKey = req.headers["authorization"];
@@ -184,6 +205,7 @@ app.post("/inbox", async (req, res) => {
184205

185206
const PORT = Number(process.env.PORT || "5181");
186207
app.listen(PORT, () => {
208+
isReady = true;
187209
console.log(`📫 notesnook inbox api server running on port ${PORT}`);
188210
});
189211
})();

0 commit comments

Comments
 (0)