cryptgeon: self-hosted private note and file sharing
cryptgeon is a secure, open source note / file sharing service inspired by PrivNote written in rust & svelte.
At a glance
- What is it?
- cryptgeon is a Rust and Svelte service for sharing encrypted text and files with view or time limits. Its client-side encryption keeps note contents unreadable to the server, while its in-memory cache simplifies expiration at the cost of persistence.
- Who is it for?
- cryptgeon fits operators who want to run their own simple sharing service and accept ephemeral storage and the limits of a cache-backed deployment. It is a poor fit for durable records, environments requiring guaranteed access-count enforcement across multiple app instances, or users who need documented rollback procedures.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Svelte, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
cryptgeon addresses temporary private sharing
cryptgeon is designed for transferring a note or file that should be available only for a limited period or a limited number of views. It is aimed at operators who prefer to host the service themselves and at users who need a lightweight alternative to a conventional document-sharing account. The README describes it as inspired by PrivNote and says the project includes a server, a web page, and a CLI client. It also links to an official Raycast extension.
The service supports text notes and file uploads. A sender can also request view constraints or an expiration constraint. These controls are useful when the objective is to reduce the useful lifetime of shared content, rather than manage an ongoing folder of documents. cryptgeon does not present itself as a document repository with accounts, version history, or durable storage. Its design centers on a temporary payload associated with a generated identifier.
That boundary matters. Once a payload is no longer available in the cache, the README does not provide a persistence or recovery path for it. The service is better suited to one-time transfer of secrets, short-lived instructions, or files that exist elsewhere. It should not be treated as the sole store for business records merely because it can accept files.
XChaCha20-Poly1305 encryption happens before storage
The security model depends on separation between the note identifier and its encryption key. According to the README, cryptgeon generates a 256-bit `id` and a 256-bit `key`. The identifier is used to save and retrieve the note, while the payload is encrypted in the client with XChaCha20-Poly1305 before being sent to the server. The server receives encrypted bytes and metadata but does not receive the encryption key, so the README states that it cannot decrypt the content.
The practical flow therefore begins in the client, not in the backend. The client creates or receives the key material, encrypts the text or file, submits the ciphertext under an identifier, and shares the retrieval information with the recipient. The recipient uses that information to retrieve and decrypt the payload. The README describes the stored body as raw bytes containing MessagePack and ciphertext. It also defines `EXTRA_SIZE_LIMIT` for an opaque `extra` payload that can hold values such as key derivation parameters.
This architecture reduces the server's role to storage and retrieval, but it also makes link handling important. The README does not explain every detail of how a generated retrieval URL is formed, nor does it document a recovery procedure for a lost key or incomplete link. Users should not infer that possession of the service address alone is sufficient, or that a server operator can recover inaccessible content.
Redis or Valkey storage makes retention ephemeral
cryptgeon stores data in memory and does not persist notes to disk. The repository's `docker-compose.yaml` runs `valkey/valkey:9-alpine` with persistence commands disabled: `valkey-server --save "" --appendonly no`. A temporary filesystem mounts `/data`, and the Compose file comments that this configuration is required to keep Valkey in RAM only.
The backend reads its cache location from `CACHE`, whose default is `redis://cache/`. The container image sets the same value, allowing the app service to reach the Compose cache service by hostname. This arrangement removes normal disk persistence, but it also means cache lifecycle governs data lifecycle. The README does not claim that restart survival, replication, or recovery is provided for notes.
Operators can set `SIZE_LIMIT`, which defaults to `1 KiB` in the environment table and is set to `4 MiB` in `docker-compose.yaml`. Accepted values follow byte-unit syntax, and the documented maximum is `512 MiB`. Since the payload is raw MessagePack plus ciphertext, the frontend displays the complete configured limit. `MAX_VIEWS` defaults to `100`, `MAX_EXPIRATION` to `360` minutes, and `ALLOW_ADVANCED` to `true`. Setting `ALLOW_ADVANCED` to `false` forces every note to allow one view.
Docker Compose starts the web service and cache
The repository provides Docker Compose files for a packaged deployment and local development. From a checkout, the documented package script can start the development stack. This command starts the services defined in `docker-compose.dev.yaml`:
pnpm run docker:upFor a deployment based on `docker-compose.yaml`, the app exposes host port `80` and maps it to container port `8000`. It also sets `SIZE_LIMIT` to `4 MiB`. The Compose configuration defines an application health check against `http://127.0.0.1:8000/healthz`, so the backend is expected to report that endpoint as healthy. The repository's Dockerfile builds the Svelte frontend in a Node stage, compiles the Rust backend in a Rust stage, and places both artifacts into the final Alpine container.
The first direct CLI use does not require operating the web interface. The README gives this exact example:
npx cryptgeon send text "This is a secret note"The README links to `packages/cli/README.md` for further CLI documentation. It does not state which host this command uses by default, so a self-hosted operator should consult that package documentation rather than assuming that `npx cryptgeon` targets a local or public cryptgeon service. For first-time browser use, the README directs users to `https://cryptgeon.org` as the live service or demo.
A single cache instance avoids documented view-count races
cryptgeon guarantees view counts with one running application instance, according to the README. The limitation appears when multiple application instances connect to the same cache. Concurrent access can produce race conditions, allowing a note to be retrieved more times than its configured view count permits.
This is a deployment constraint, not a minor implementation detail. If an operator scales the `app` service horizontally against one Redis or Valkey cache, strict one-view semantics should not be assumed. `docker-compose.yaml` defines only one `app` service and does not document a distributed locking protocol or an atomic policy that changes this behavior. The repository also does not provide a multi-instance test result that would extend the guarantee.
The project avoids external database dependencies in its documented stack, but that simplicity comes at a cost. A single-instance deployment is easier to reason about for view enforcement, while restart or cache loss can remove temporary data. The README does not document a backup procedure because the stated architecture does not persist note bodies to disk. For deployments that depend on predictable deletion semantics, this is acceptable only if losing temporary content is an intended property.
cryptgeon differs from durable storage and managed secret tools
The approach differs in two visible ways. Object storage keeps the payload in durable backend storage and delegates link lifetime to the storage service and its policy engine. cryptgeon keeps the payload in a cache, encrypts it before submission, and combines encryption with view and time constraints in the sharing interface.
A password manager's secure note feature is another alternative for short secrets, but it differs in delivery model. Password-manager notes remain within an account system and are retrieved through that service, whereas cryptgeon is designed around a generated sharing identifier and a transfer URL. The README does not provide a feature-by-feature comparison with either category, so the relevant distinction is architectural: persistent object storage and account-backed secret storage solve different workflows from cryptgeon's ephemeral sharing model.
cryptgeon is the wrong tool when an attachment must survive service restarts, require recovery after cache loss, or be governed by durable retention rules. It is also unsuitable when multiple application instances must enforce the exact maximum view count documented by the project. Its best fit is a deliberately temporary transfer where client-side confidentiality matters more than record keeping.
MIT licensing and recent release tags affect adoption
The repository is licensed under the MIT License. That permits use and modification under the license's stated conditions, although this review does not provide legal advice or interpret obligations beyond the license identifier. Operators are responsible for reviewing the `LICENSE` file and any separate terms that apply to deployment, reverse proxying, or integrated components.
The repository is not archived, and the last push was on 2026-10-09. Recent release tags include `v3.0.0-rc.4` from 2026-09-19, `v3.0.0-rc.3` from 2026-09-12, and `v3.0.0-rc.2` from 2026-09-12. These are release candidate tags, so the README's deployment commands should be matched to the image tag an operator intentionally selects. `docker-compose.yaml` uses `cupcakearmy/cryptgeon:3` rather than one of the release candidate tags.
Upgrade cost is not documented as a formal compatibility policy. The presence of `CHANGELOG.md` gives operators a release-history file to inspect, but the README does not state whether database migrations are required, how older notes behave after an upgrade, or whether rollback is supported. Because notes are held in memory, upgrade planning also includes accepting that cached payloads may not survive the process.
Editorial conclusion
cryptgeon fits operators who want to run their own simple sharing service and accept ephemeral storage and the limits of a cache-backed deployment. It is a poor fit for durable records, environments requiring guaranteed access-count enforcement across multiple app instances, or users who need documented rollback procedures. Before adoption, verify that XChaCha20-Poly1305 client-side encryption, the chosen view and expiration settings, and the in-memory Redis or Valkey model meet the intended threat model and availability requirements.
Frequently asked questions
Can the cryptgeon server decrypt a shared note or file?
No, according to the README. The client encrypts content with XChaCha20-Poly1305 before sending it, while the encryption key is not sent to the server.
Does cryptgeon persist shared notes and files to disk?
No. Data is stored in memory, and `docker-compose.yaml` disables Valkey disk persistence with `valkey-server --save "" --appendonly no`.
Does cryptgeon support files and expiration limits?
Yes. It supports text and file sharing, with `MAX_EXPIRATION` defaulting to `360` minutes and `SIZE_LIMIT` defaulting to `1 KiB` in the README's environment table.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cupcakearmy-cryptgeon)Was this useful?