Skip to content

Commit cd1dc81

Browse files
committed
Add security documentation
1 parent 5a49788 commit cd1dc81

5 files changed

Lines changed: 680 additions & 0 deletions

File tree

SECURITY.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ repository. Include:
2020

2121
We aim to acknowledge reports within 3 business days and to ship a fix or
2222
publish a coordinated advisory within 30 days of confirmation.
23+
24+
## Security Documentation
25+
26+
For the current product security model and deployment guidance, read:
27+
28+
- [docs/how-to/secure-self-hosting.md](docs/how-to/secure-self-hosting.md)
29+
- [docs/reference/security-reference.md](docs/reference/security-reference.md)
30+
- [docs/explanation/security-model.md](docs/explanation/security-model.md)

docs/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@ If you are new to ZenNotes, read these first:
2525

2626
- [Self-Host with Docker](./how-to/self-host-with-docker.md)
2727
- [Connect Desktop to a Remote ZenNotes Server](./how-to/connect-desktop-to-remote-server.md)
28+
- [Secure Self-Hosting](./how-to/secure-self-hosting.md)
2829

2930
## Reference
3031

3132
- [Settings Reference](./reference/settings-reference.md)
3233
- [Vault and Folder Model](./reference/vault-and-folder-model.md)
3334
- [Runtime and Package Map](./reference/runtime-and-package-map.md)
35+
- [Security Reference](./reference/security-reference.md)
3436

3537
## Explanation
3638

3739
- [How ZenNotes Works](./explanation/how-zennotes-works.md)
40+
- [Security Model](./explanation/security-model.md)
3841
- [Monorepo Architecture](./monorepo-architecture.md)
3942
- [Web Architecture](./web-architecture.md)
4043

docs/explanation/security-model.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Security Model
2+
3+
This document explains how ZenNotes approaches security across desktop, web, and self-hosted deployment.
4+
5+
It is intentionally candid. The goal is not to sound maximally hardened. The goal is to explain what ZenNotes actually protects, what it assumes, and where the current boundaries are.
6+
7+
## The core security assumption
8+
9+
ZenNotes is easiest to reason about when you treat it as:
10+
11+
- a single-user notes system
12+
- built around plain files on disk
13+
- optionally reachable over a network through a trusted self-hosted server
14+
15+
That assumption shapes almost every security choice.
16+
17+
ZenNotes is not currently built as:
18+
19+
- a public multi-tenant SaaS security boundary first
20+
- a zero-trust collaboration system
21+
- a product that expects arbitrary untrusted browser clients and untrusted filesystem tenants
22+
23+
## Why the security model changed
24+
25+
When ZenNotes was effectively only a local desktop app, the threat model was simpler.
26+
27+
Once ZenNotes gained:
28+
29+
- a self-hosted browser mode
30+
- a Go server
31+
- desktop remote workspaces
32+
- Docker deployment
33+
34+
the old assumptions stopped being good enough.
35+
36+
The biggest changes were:
37+
38+
- moving browser auth to session cookies
39+
- treating browse roots as real authorization boundaries
40+
- separating host config from vault config
41+
- keeping desktop remote credentials out of renderer-visible state
42+
- making Docker defaults more restrictive
43+
44+
## The system has three main trust zones
45+
46+
### 1. Local desktop runtime
47+
48+
The desktop app is still a trusted local application.
49+
50+
It can:
51+
52+
- access the local vault directly
53+
- talk to the remote server from the main process
54+
- use native integrations
55+
56+
Its protection story is mostly about:
57+
58+
- isolating the renderer from privileged APIs
59+
- preventing untrusted renderers from using privileged IPC
60+
- storing secrets outside plain config where possible
61+
62+
### 2. Self-hosted server
63+
64+
The Go server is the trust boundary for browser access.
65+
66+
It is responsible for:
67+
68+
- authenticating clients
69+
- scoping accessible filesystem roots
70+
- validating origins
71+
- serving only what the browser should get
72+
73+
The server is where a browser request becomes a filesystem operation, so the server boundary matters more than the client boundary in self-hosted mode.
74+
75+
### 3. Host filesystem
76+
77+
The host filesystem is the real source of truth for notes.
78+
79+
That is a product strength, but it also means security is not only about HTTP. It is also about:
80+
81+
- what directories the server can reach
82+
- whether Docker mounts are scoped correctly
83+
- where secrets are stored
84+
- whether the app accidentally writes sensitive operational config into a synced vault
85+
86+
## Why browser auth uses sessions now
87+
88+
Using a bootstrap token in URLs or long-lived browser storage creates avoidable leakage points.
89+
90+
So the current browser model is:
91+
92+
1. the user enters a bootstrap token once
93+
2. the server verifies it
94+
3. the server issues a random session
95+
4. the browser continues with an `HttpOnly` cookie
96+
97+
This matters because it reduces exposure through:
98+
99+
- local storage
100+
- copied URLs
101+
- logs and history
102+
- accidental token reuse in browser-visible state
103+
104+
It is not full identity and account management. It is a tighter single-user session model.
105+
106+
## Why browse roots matter so much
107+
108+
ZenNotes now has a server-side directory browser and remote vault switching.
109+
110+
That is useful, but it is also the most obvious place where convenience can turn into overreach.
111+
112+
So the current design treats browse roots as a real boundary:
113+
114+
- resolve the requested path
115+
- resolve symlinks
116+
- reject anything outside allowed roots
117+
118+
This is one of the most important practical controls in the self-hosted product, because it narrows what a remote client can even ask the server to consider a vault.
119+
120+
## Why Docker is part of the security story
121+
122+
Docker is not only a convenience story for ZenNotes. It is also part of the security posture.
123+
124+
The current default container setup:
125+
126+
- binds to loopback
127+
- runs non-root
128+
- drops Linux capabilities
129+
- uses `no-new-privileges`
130+
- keeps the root filesystem read-only
131+
132+
This does not make Docker magically secure. But it does mean the default self-hosted path is narrower and safer than a broad all-interfaces, writable-root, root-running container.
133+
134+
## Why desktop secrets stay in the main process
135+
136+
For remote workspaces, the risky design would be:
137+
138+
- renderer holds raw server token
139+
- renderer fetches server directly
140+
141+
ZenNotes instead pushes that work toward:
142+
143+
- main-process networking
144+
- main-process asset proxying
145+
- secret-store-backed credential persistence
146+
147+
That is the right direction because the Electron renderer should not be treated as the place where you casually keep long-lived remote credentials.
148+
149+
## Why the vault must not store server secrets
150+
151+
ZenNotes intentionally supports:
152+
153+
- shared mounted vaults
154+
- synced vaults
155+
- desktop plus browser against the same files
156+
157+
That means the vault is a terrible place to hide operational server secrets.
158+
159+
So the current direction is:
160+
161+
- vault stores vault behavior
162+
- host config stores host/server operations and auth secrets
163+
164+
That separation is fundamental. Without it, the product would keep smuggling server operational state into the same content tree users want to sync and inspect.
165+
166+
## The current honest limitations
167+
168+
ZenNotes is more hardened than before, but a few important tradeoffs remain.
169+
170+
### Electron sandboxing is not fully done
171+
172+
Desktop currently keeps:
173+
174+
- `contextIsolation: true`
175+
- `nodeIntegration: false`
176+
177+
but still uses:
178+
179+
- `sandbox: false`
180+
181+
That is not ideal. It is an acknowledged technical debt item driven by the current preload path.
182+
183+
### CSP still has exceptions
184+
185+
The current CSP is materially better than a meta-tag-only approach, but it still contains:
186+
187+
- `unsafe-eval`
188+
- `unsafe-inline` for styles
189+
190+
That reflects the current rendering/editor stack, not an ideal end state.
191+
192+
### The product is not pretending to be multi-tenant SaaS-hard
193+
194+
ZenNotes is strongest today when used as:
195+
196+
- local desktop app
197+
- single-user self-hosted web app
198+
- remote desktop-to-server setup in a trusted deployment
199+
200+
That is the practical security promise.
201+
202+
## The right mental model for operators
203+
204+
If you are deploying ZenNotes for browser use, think like this:
205+
206+
- the server is privileged
207+
- the mounted vault is valuable
208+
- the browser is a client, not the authority
209+
- the reverse proxy is your public perimeter if you expose it remotely
210+
211+
That model will lead you to the right defaults more often than treating ZenNotes like a static web app that happens to read files somewhere.
212+
213+
## Related docs
214+
215+
- [Security Reference](../reference/security-reference.md)
216+
- [Secure Self-Hosting](../how-to/secure-self-hosting.md)
217+
- [How ZenNotes Works](./how-zennotes-works.md)

0 commit comments

Comments
 (0)