Zum Inhalt springen
Under the hood

For the reader who does not believe marketing copy.

Everything on the rest of this site, restated as mechanism — names, curves, sizes, thresholds, and the places where a limit exists, including the ones that are not flattering. If you find something here the code does not do, we would rather hear about it than not.

This page is published in English only, in every language the rest of the site is offered in. A translated description of a cryptographic construction that has drifted from the code is worse than an exact English one.

Each claim below opens in one breath and expands into the full mechanism. The short version is honest; the long version is checkable. Read whichever one you came for.

Proof

The signed history

Every change is an action in a hash-linked, signed chain. The byte layout is public, a test pins it to the shipping code, and you can rebuild any hash yourself.

Show the mechanism

Every change is an action in a directed acyclic graph. An action carries its parent hashes, the author node, a per-author sequence number, a UTC timestamp and the payload. Its identity is BLAKE3-256 over those fields concatenated in a fixed order — parents, author, sequence, timestamp, payload — with the signature deliberately excluded, so signing never changes the hash. The signature is Ed25519 over the 32 raw digest bytes, not over the preimage and not over its hex rendering. The byte layout is published as a spec, aingle-dag-action-v1, so you can rebuild the preimage yourself; a test pins the published spec against the code that actually hashes, so the two cannot drift apart in silence.

Removal is a retraction, not a delete

Deleting a note appends a signed retraction — history stays provable and time-travel keeps working. The one real delete, pruning, is explicit, checkpointed and marked destructive.

Show the mechanism

Deleting a note does not erase anything. It appends a signed deletion action naming what it retracts, so the earlier state stays reachable and provable and time-travel survives. One exception, stated because you would find it: a prune operation does physically remove old actions under an explicit retention policy. It never prunes the tips, and it writes a checkpoint recording what it removed — but it is a real delete, and where it is exposed to AI tooling it is marked destructive rather than hidden among the read-only calls.

What the lock proves — and what it does not

The lock means two things: the signature verifies, and the file on disk still matches it. Every failure fails closed. What it cannot prove — identity, trusted time — is stated just as plainly.

Show the mechanism

A verified lock means two things at once: the Ed25519 signature on the anchoring action verified, and the note on disk still hashes to exactly what that signature attests. Either one failing yields no lock, and every error path fails closed — no key, no graph, a malformed hash or a missing action all produce "unverified" rather than the benefit of the doubt. "Signed, then edited" is reported as its own state instead of collapsing to unsigned. What it does not prove: the key is your vault's own, generated locally, so this is a self-attestation, not an identity — there is no certificate authority and no binding to a person. The timestamp is your machine's clock, sealed inside the signed bytes; there is no timestamping authority, so anyone holding the seed could back-date. And the signing seed is stored unencrypted next to the database. It proves these bytes were ingested and signed here and have not changed since. That is a smaller claim than "authentic", and it is the one we make.

Encryption & sync

Encryption at the sync target

Each sync target gets one random master key, generated on your device and reachable only through your passphrase or a one-time recovery key. Nothing is escrowed and no reset exists — we could not read your files if we wanted to.

Show the mechanism

One random 32-byte master key per target, generated on the device. Three subkeys derived from it with BLAKE3 derive_key under distinct context strings: one for content, one for blob names, one for a public target id. Content is XChaCha20-Poly1305 with a fresh random 24-byte nonce per write and a 16-byte tag. The master key is reachable two ways, both held only by you: a passphrase wrapped with Argon2id — the argon2 crate defaults, 19 MiB, 2 iterations, 1 lane, with a 16-byte random per-target salt — stored in a keybox on the target itself so any machine that can see the folder can unlock it; or a one-time recovery key, which is the master key rendered as 56 Crockford base32 characters with a checksum. Nothing is escrowed. We hold no copy and there is no reset, which is the same thing as saying we could not read your files even if we wanted to.

What actually lands in the folder

What lands in the synced folder is sealed blobs with fixed-length names. The real path travels inside the encryption, so a name, a length or a rename leaks nothing — and tampering is detected, not silently accepted.

Show the mechanism

A blob's name is a keyed BLAKE3 hash of its vault path, Crockford base32 encoded — always 52 characters, so the length of a path leaks nothing either. The real path travels inside the sealed payload, so decrypting one blob is enough to place it: there is no manifest and no name table that could be lost or corrupted. The authentication tag covers the path, so a blob renamed or moved by anything other than the app is detected rather than silently accepted. Padding is applied to the plaintext before sealing: the padded length is the next multiple of 4 KiB up to 64 KiB, and the next multiple of 64 KiB above that. On disk a blob is that padded length plus exactly 40 bytes — a 24-byte nonce and a 16-byte authentication tag.

What an observer of that folder can still see

An observer of that folder still learns some things — how many notes, sizes within a padding bucket, how often each pseudonymous blob changes — and this section names all of them. Never a title, a path or a word of content.

Show the mechanism

Stated here because you would find it anyway, and because a limitation you discover for yourself is worth far more doubt than one we hand you. Someone who can read the synced folder learns: that it is an Altretta target, because the header file is plaintext on purpose so a second machine can find the salt; the number of notes, since there is one blob each; and each file's size to within its padding bucket. Because a blob's name is a deterministic function of its path, every note keeps the same name for its whole life — a stable pseudonym. That determinism is what lets two machines agree where a note lives without a shared index, and it is also what lets an observer watch how often you edit any single note, note by note, from timestamps alone — without ever learning which note it is. Deletion is the one place this was worse and was fixed rather than explained away: a deleted blob is moved into a trash directory instead of being unlinked, and until 0.9.1 it kept its name and gained the millisecond it was removed, which handed a single snapshot of the folder both an exact deletion time and the pseudonym the note wore while it was alive. It is now filed under a fresh random id and the real path is read back out of the sealed payload, so what remains readable is the number of deletions and nothing else. The public target id is a stable fingerprint linking two folders to the same key. None of it discloses a title, a path, or a word of content.

Grounded answers

What makes the assistant refuse

"Grounded" requires the best passage and corroborating ones to clear calibrated thresholds. When your notes cannot support an answer, no model is called at all — refusing is the default, and answering anyway is a switch only you can flip.

Show the mechanism

Retrieval embeds your question, over-fetches from the memory index, keeps only chunk entries and re-ranks them by pure cosine similarity — deliberately discarding the composite recency-and-importance score the memory layer would otherwise apply, because relevance to the question is the only thing that should decide a citation. A verdict of "grounded" requires the best match to clear the high threshold and at least 2 chunks to clear it: corroboration, not one lucky passage. With the neural embedder the product ships, those thresholds are 0.80 and 0.77, calibrated against a measurement that unrelated pairs top out near 0.76. Between the two is "weak"; below both is "ungrounded". The answerability gate is then: at least one visible source after your folder exclusions have been applied, and — if you asked for grounded answers only — a verdict of exactly "grounded". When that gate fails, no model call is made at all. Answering anyway is a separate flag that is never inferred on your behalf, and when you set it the weak passages are withheld from the model so it cannot cite what did not qualify.

Code & documents

The symbol graph

Twenty languages are parsed into a signed symbol graph with typed edges that carry a confidence tier. Skipped files and budgets are recorded as queryable facts rather than hidden.

Show the mechanism

Twenty languages have symbol extractors, each a tree-sitter parse plus a query: Rust, TypeScript, JavaScript, Python, Go, Swift, Java, C, C++, C#, Kotlin, PHP, Ruby, Dart, Scala, Objective-C, Bash, Lua, R and SQL. Symbols get canonical identifiers and are emitted as signed triples through the same path as everything else in the graph. Edges are typed — defines, imports, references, calls — and carry a confidence tier: a call resolving to exactly one candidate is high confidence, while a call with homonyms and every bare reference are marked uncertain rather than asserted as fact. Files over 2 MiB are recorded as skipped rather than quietly ignored — the skip is itself a node in the graph you can query — and a project is budgeted at 750,000 symbols. Drift compares a note's last-written time against the newest signed change to the symbol it documents. The limitation, which the source states plainly: that time is the filesystem mtime, so a checkout, a restore or an rsync that rewrites mtimes will change the verdict.

Reading documents, and what gets dropped

Word, PowerPoint, PDF and spreadsheets are read with their structure intact, so an answer can cite the page, the slide or the clause — not just a filename. Caps are enforced by bounded reads, and gaps are named.

Show the mechanism

Word, PowerPoint and their OpenDocument equivalents are parsed in pure Rust — no rasterising, no OCR, no native dependency. Spreadsheets go through calamine. PDFs use the pdf.js text layer; rasterising with OCR is a separate, opt-in tier that runs only on pages with no usable text, and pages left unread are recorded as debt rather than silently dropped. Extraction emits citation anchors — a page marker per PDF page, a heading per slide, a sheet-and-row anchor per spreadsheet block, and a section anchor for numbered sections in a regulation, keeping the numeral in the heading because it is the only stable name that section has. Slides are ordered numerically, so slide 10 follows slide 9 rather than slide 1. Caps are enforced by bounded reads instead of trusting a declared size: 32 MiB per XML part, 128 MiB per archive, 16 MiB of emitted text, 5,000 slides, and 50,000 rows in a spreadsheet — a table inside a document is capped far lower, at 4,096 rows by 256 columns. A gap worth naming: spreadsheets report truncation as a warning and the other converters do not, so a very large document can be trimmed quietly. The extracted text is written as an ordinary companion Markdown file beside the source, so the normal ingest signs it like any other note — there is no extraction database, the companion file's recorded source hash is the idempotency record.

Ownership & team

Where everything lives on disk

Your folder holds your notes and almost nothing else; everything derived lives outside it and is always safe to delete and rebuild. If we disappeared tomorrow, the app keeps opening, reading, writing, searching and answering.

Show the mechanism

Your notes are Markdown files in your folder. The only thing the app adds inside it is a hidden control directory holding the vault format stamp and, for a team vault, the keyring. Everything derived — the graph database, the search index, the snapshot store, the signing seed — lives outside the vault in application data, and the app refuses to start if you point the database inside the vault, because its own writes would trigger a re-ingest loop. That separation is why removing the app leaves your folder untouched, and why the derived index is always safe to delete and rebuild. It is also, honestly, why version history does not travel with a synced folder today. Entitlement is decided offline: a signed token verified on your machine against a key compiled into the app, against your machine's clock, with no server in the path — and the app additionally asks the licence server once per launch whether anything changed, so a renewal or a cancellation shows up promptly. That call is best-effort in one direction only: a transport failure never denies, an expired licence keeps working offline for fourteen days, and past that it drops to Free rather than locking. If we disappeared tomorrow, the app keeps opening, reading, writing, searching and answering.

The team vault, and the thing it does not hide

Team note contents are end-to-end encrypted under per-member keys, and removing someone rotates the key. Filenames and folder structure are not encrypted — a documented limit, stated here rather than discovered.

Show the mechanism

A team vault is a second, encrypted store beside the plaintext one. Note contents are sealed with XChaCha20-Poly1305 under a vault key that is handed out through an owner-signed roster: each member holds an X25519 keypair and the key is wrapped to their public key, so it is never transmitted in the clear, and removing someone rotates it. The limit is stated in the source under a heading that calls it a documented metadata leak, and it is repeated here for the same reason: filenames and directory structure stay in plaintext. The encrypted store mirrors the plaintext tree path for path, so anyone holding it learns the shape of the vault and the title of every note, and only the contents are unreadable. This is the one place where the personal sync target is strictly stronger than the team vault — there, the names are hashed too. Encrypting team filenames is deferred, not done.

What leaves the machine, and when

Everything below is something you switch on. None of it happens to someone who installs the app and writes notes. Naming all of it is the only way the privacy claim on the front page stays true, so this list is meant to be complete rather than reassuring.

  • The AI you connect. Altretta hands your AI client the passages an answer is built from. There is no model inside the app, so if that client talks to a cloud provider those passages reach it under your own account and their terms, not ours. Retrieval, embedding and the grounding verdict are local either way; point the client at a model running on your own machine and nothing leaves at all.
  • Publishing a site. Choosing to publish uploads the pages you selected — rendered as HTML, with the images they reference — to our server, so other people can open them at a public address. That is the whole point of the feature and it is the one place note content sits on our infrastructure. The selection starts empty, a note marked private is dropped even if you picked it, and there is a local export that writes the same site to a folder you host yourself.
  • Encrypted sync. What lands in your Dropbox, OneDrive, network share or USB stick is ciphertext with hashed file names, and our servers are not in the path at all — the folder is yours and you chose it.
  • Notion and Confluence import. Reading from those accounts means talking to them, with credentials you supplied.
  • Signing in and licensing. Activating a licence, checking whether it is still valid once per app launch, and signing in through Google, Microsoft or GitHub if you choose to.

Do not take our word for it

The app reaches the network in a small, boring set of places and you can watch every one: a check for updates against a static file on our download CDN, and — only if you use them — your own AI provider, your Notion or Confluence account, our licence and account server, the plugin hub, and whichever identity provider you sign in with. Point a network monitor at it and see.

How to check all of this yourself

The engine is open source. github.com/ApiliumCode/aingle holds the signing, hashing, graph and grounded-retrieval implementation, under Apache-2.0 for individuals, academia and organisations under $1M revenue, with a commercial licence above that. The application itself is not open source, and we would rather tell you that than imply otherwise.

  • Reproduce a content hash: run any BLAKE3 tool over a note and compare it with the hash the app shows you. There is a pinned test vector in the public source to check your tooling against first.
  • Rebuild an action hash and verify its signature independently: the byte layout is published as a spec, and the public test suite exercises tamper-rejection and the fact that signing does not change the hash.
  • Confirm there is no telemetry: grep the engine source for the usual analytics and crash-reporting names, then list every URL literal in it. What comes back is your own AI provider, your own connectors, our licence, publishing and plugin-hub endpoints, the identity providers you can sign in with, and the update manifest — nothing else.
  • Watch the network: open a vault with a monitor running. Nothing should be contacted until you configure a model or deliberately click an account action.
  • Check a published page without trusting us: a published site carries its own verifier, which rebuilds the action preimage in your browser and checks the signature offline. Break one byte of the page and watch it refuse.
  • Confirm no reset exists: forget the passphrase and lose the recovery key on a scratch target. Nothing in the app, and nothing we hold, can open it again.
Read the engine source