Skip to content

Security: rgdevment/Tisty

SECURITY.md

Security policy

Tisty stores tasks, notes and documents — material that is often personal and sometimes confidential. This document explains what the design protects, what it deliberately does not, and how to report a problem. For what is collected and where it goes, see PRIVACY.md — the short answer is nothing, and nowhere you did not choose.

Reporting a vulnerability

Do not open a public issue. Use either private channel:

  • Email rgdevment@tisty.app, subject [SECURITY] short description.
  • GitHub Security Advisory — the Security tab, Report a vulnerability.

A vulnerability in Folio by Tisty, the separate service for sharing by link, goes by the security policy in its own repository.

Useful in a report: what an attacker could do and to whom, steps to reproduce, the affected version, and your operating system. A proof of concept helps but is not required.

You will get an acknowledgement within 48 hours. Fixes for anything serious are aimed at days, not weeks. You will be credited in the release notes unless you prefer otherwise.

Good-faith security research will never be met with legal threats, and a report will never be dismissed for being inconvenient.

What the design protects

  • The data never leaves your machine unless you tell it to. No account, no telemetry. Nothing you keep reaches a service of ours unless you press Share on a document; then that document reaches Folio sealed, so nothing readable sits on our side to breach. Tisty makes one check of its own — a daily GET for a version manifest and one for its signature, carrying nothing but the version you are running — and two more requests only if you press the button that offers you an update. A copy from the Microsoft Store makes none of them unless the Store's catalog does not answer: it asks Windows what the Store has for it and reads that catalog. It is written out in full in PRIVACY.md, down to the headers, and so are the few others — the guide, the help's pictures, a link's preview, the pictures from the internet in the viewer of a file from outside, the signed cloud.json at most once an hour while you sync, and those of Folio.

  • A cloud account is reached only if you choose it. Tisty can sync straight with your own Google Drive since 1.30, your own OneDrive since 1.31 and a WebDAV server of your own since 1.32; Dropbox is reached only through a folder its desktop program keeps in step. With Google it asks only for drive.appdata — so it sees its own hidden space and nothing else in your Drive — and gives back any access granted wider than that; with Microsoft it asks only for its own Apps › Tisty folder and refuses a sign-in that grants more. A WebDAV server is reached only over https, except on this very computer, never through a redirect, and only inside a Tisty folder of its own; it is not connected unless it proves, before anything is kept, that it refuses to write over a copy it was not told about. Tisty keeps the access it was given, or the WebDAV password, in the system keychain on macOS and, on Windows, in a file sealed for your user with DPAPI under your profile that uninstalling removes, and masks tokens, passwords and signed addresses in its own log. Only the window holds it and only the window connects an account; assistants never reach the network. Syncing goes to no server of ours. A daily allowance per computer keeps one copy from spending the project's shared quota, and the one remote switch that can lower it or pause syncing, cloud.json, is refused unless it is signed with minisign against a key fixed in the build, is no larger than 16 KiB and is newer than the last one heard. You can withdraw Tisty's access from the window, from your Google or Microsoft account, or by removing the password on your server, at any time. What it asks and sends is in PRIVACY.md.

  • An update is verified before it is installed. What the button fetches is checked against a public key compiled into the copy already on your machine. A build signed with any other key is refused, so a release that is not the maintainer's cannot arrive this way, whatever it claims to be. Two narrower guards sit in front of that one: the address an update is fetched from must be where this project's releases live, not merely whatever the feed named, and the signature must name the very version offered, so an older signed installer cannot be passed off as a newer one; and the download is given a deadline, so a server that answers slowly forever is eventually hung up on. A copy kept by the Microsoft Store never fetches an installer at all: it asks the Store what it has and lets the Store install it, so what stands behind that package is Microsoft's signature rather than the key below. The rule that nothing older than what is running may be offered belongs to the feed: what the Store offers is the Store's to decide, and Tisty shows that offer whatever number comes with it.

  • What the world sends is read to a limit, never to the end. Two kinds of request go to an address Tisty did not choose, each asked for by a button: one preview of one link, and the pictures from the internet in the viewer of a file from outside. A page is read to 256 KiB and the picture it offers to 400 KiB — read to the cap off the wire, not fetched whole and measured afterwards, so how much of your memory a page takes is not that page's decision. The request has a deadline of six seconds, the answer is parsed without executing anything, and what is kept is bounded by weight as well as by count, so the cache cannot quietly become a folder of somebody else's pictures. A picture in the viewer is read to 20 MB under a deadline of ten seconds, is taken only if the answer says it is a picture, and is kept nowhere. An address that leads to this computer or to your own network is refused, and checked again on each redirect.

  • What reaches Tisty from outside is a stranger's word. Tisty answers to two addresses, and any page can fire them. tisty://open?path=… shows, read-only, only an absolute path on a local disk to an existing .md, .markdown, .txt, .ics or .ical file, with no .. in it. tisty://edit?link=… takes only an https link on tisty.cl, folio.tisty.app or the private address Folio signs in domains/hosts.json. Tisty asks you before anything leaves, and then asks that signed address, never the one the link names. That link carries the key of a private document, so it is not written to Tisty's log.

  • Synchronisation is yours. Tisty copies files into a folder you name, and whatever already keeps that folder in step between your machines is what moves them — or into a cloud of your own that it reaches itself. Syncing has no Tisty-operated backend at any tier, and no transport of ours to attack: with a folder, the code that syncs reads and writes local paths, and with a cloud it speaks only to that provider or that server, plus, at most once an hour, the signed cloud.json on GitHub described in PRIVACY.md under The remote switch. Never a server of ours.

  • A cloud can be encrypted so that its provider cannot read it. Since 1.36, what Tisty keeps in Google Drive, OneDrive or a WebDAV server can be sealed with a key that comes from a 24-word phrase only you hold. Neither the provider nor this project holds that key. It is described in its own section below.

  • An older Tisty stops instead of writing over a newer one. Since 1.34, every place Tisty syncs through says in its tisty.toml the oldest release that may write there (needs), and every release since 1.30 reads it: one older than that stops and asks to be updated, and neither reads nor writes. An encrypted cloud raises it, so a release that does not know encryption never writes plain files beside the sealed ones. Each machine also writes into the log the release it runs, so Tisty can say which computer is behind.

  • A Tisty is one person's. Since 1.44, a store belongs to the first identity that vouched for one of its machines and still stands by it. Another person's machine that writes into the same place is still named as the writer of its lines, but it never signs as you and its phrase vouches for no machine of yours; Merge the two is refused when the other history answers to another person's phrase, and a machine new to your log has to sign what it writes. A copy of someone else's writing stays theirs whichever way it arrives — copied, joined, carried in a parcel or shared — with its pages, versions, languages and files.

  • No plugin system. Nothing loads third-party code into the process.

  • unsafe is forbidden at the workspace level, not merely discouraged. The window's crate is the one exception, and a narrow one: it denies unsafe too, and lets it in only where the operating system offers no other door — the webview on both systems, sysctl on macOS, and on Windows the shell, the imaging component, a program's version information, the notice that the path changed, DPAPI, the Store's dialog, asking to be reopened after an update and the warning that comes before Windows closes it — each function marked one by one, in six files, and a test fails if a mark appears in any other.

  • A release is held to what CI already proved. A tag on a commit that did not pass CI on main is not released. The updater's signature is checked against the key the build ships before anything is published, and every file on the releases page comes with a SHA256SUMS and a build-provenance attestation, so a download can be traced back to the workflow that made it.

  • The window is fenced by a content policy. Everything it runs is shipped inside the program: no script from anywhere else, no page from anywhere else, and the only address it may talk to is the local channel to the Rust side. Two openings are worth naming because they are real. Scripts may compile WebAssembly, which is what draws the PDF you see before exporting it; and a frame may show a blob:, which is that PDF, built in memory on your machine and never fetched. Neither lets remote code in: a blob has no origin to be loaded from, and there is no path by which one arrives.

    Nothing a document holds ever runs. A widget block is not code at all: it is HTML and CSS from a list the contract fixes, a block with anything else is shown as its code and never drawn in part, and what passes is drawn in a frame with no scripts, under a policy that lets it load nothing. An attached .html file is a file like any other: a card with its name and size, and Tisty never runs it or draws it. Opened from its card, it goes to the program your system chose for it, outside Tisty. On Windows every window also starts WebView2 with no name it can resolve and no WebRTC over UDP, as a second lock. macOS has no such arguments, which holds because nothing foreign runs there to reach for them.

What it deliberately does not protect

Being explicit here matters more than sounding reassuring.

  • Data is stored in plain text on your computer. Tasks, notes and documents sit readable on disk, protected only by your operating system's file permissions. At-rest encryption on disk was evaluated and rejected: it would put every reader of your data behind a key Tisty would then have to manage, and the promise here is that your files stay readable with tools you already have. Encrypting your cloud does not change this: it seals only the copy the provider holds, and each of your computers decrypts what it brings down and keeps it plain on its own disk. A folder kept in step by a desktop client is never encrypted.

  • An append-only log does not forget. If a credential ends up in a task, deleting the task removes it from the current state but not from the history underneath — and if it already travelled to the shared folder, it is on the other machines too. Prevention is the only real defence: keep anything sensitive in the private/ folder, which never leaves the machine.

  • The installer and the feed are both signed. Each installer is signed with the version it was cut for, and Tisty requires that version to match the one it was offered, so an older Tisty — genuinely signed, genuinely ours — cannot pose as a newer one. Every file of the feed — the manifest that announces versions (release-manifest.json) and the channel files the updater reads (latest.json, candidate.json) — carries a minisign signature of its own, under a key kept apart from the updater's and only where the release publishes the feed. A copy built with its public half announces no version from a manifest it cannot verify, and installs nothing unless the channel the updater read is the very one that key signed. What a signature cannot do is make the feed speak: whoever could write to it could still leave it unchanged. Copies from before the feed was signed read it as they always did, and update the same way.

  • The updater's signing key is a single point of failure. One private key signs every release, it is held by the maintainer alone, and the format offers no way to rotate it: the public half is already compiled into every copy out there. If it leaked, a build signed with it would be accepted by installed copies; if it were lost, those copies would go on working and simply stop being able to update themselves. Either way the fix is a new release installed by hand, and it would be announced on the releases page. None of this reaches a copy kept by the Store, which stands on Microsoft's signature instead — a single point of its own, but not one this project holds.

  • Whoever holds the shared folder holds your data. Syncing through a provider's folder means that provider stores your tasks, under their terms. That is the trade you make when you turn it on, and Tisty states it rather than hiding it. An encrypted cloud narrows it to what the encryption cannot hide, listed in its section below.

  • Anyone with access to your user account has access to your data. Tisty adds no second authentication layer on top of your operating system's. On Unix it does narrow the permissions it controls — its directories to 0700 and its files to 0600, owner only — but that stops another local account, not you, and not anything running as you. On Windows the folder that holds the signing key and the store's secret is cut off from the permissions it would inherit, and your account is given full control of it. The whole .tisty folder in your profile is treated the same way and hidden, so other accounts on the computer cannot open it and it does not sit in plain sight where it could be moved or deleted by mistake. The system account keeps its access, so a backup tool that runs as a service still copies it; administrators lose the access they inherit from your profile, but can still take ownership of it, as of any folder. The key folder inside it stays yours alone. On other systems, what keeps another account out is that your profile's own folders are yours.

  • Whoever can write to the shared folder can try to write to your history. Each machine signs what it writes: an Ed25519 key of its own, kept in the private/ folder of its local configuration — never synced, never in a backup — signs every write to its log, in the same line and the same write as what it signs, so a log never travels ahead of its signature; logs written before the 1.25 keep theirs in a file beside them, and the first signed write answers for them too. A machine that has said what it signs with owes a signature from then on, and a history written at the signed schema or later that arrives with none is not taken in at all. A machine whose key nobody here has confirmed is not trusted on the folder's word: what it writes waits until you confirm its key, in the window, or until you let that computer into an encrypted cloud with the twenty digits it shows, which confirms its key in the same step. If that computer then takes up the cloud's history, it does so under a new name that signs with the same key, and the computer that took its digits answers for that name once: within a week of the digits, only while the first name never wrote in the cloud, and never once you removed either name. Your other computers confirm it as any new one. Those confirmations are kept on this machine only, and a key once confirmed is replaced only on its own word: a change of key that the confirmed key itself signed is followed, and a new key nobody vouched for still waits for you. Nothing in this version changes a key; it only reads such a change, so that a later one can. The same word does not help against a key already stolen, since whoever holds it can sign a change too: that machine is removed and confirmed again by looking. The one moment the folder is taken on its word is the first time this machine reaches a folder, before it has confirmed any other — its own earlier name and its assistants do not count, which is where a computer let into an encrypted cloud stands once it takes up the cloud's history: the keys of the machines already writing there are adopted as found, so the folder you join should be one you trust.

    The signatures answer for the log, and not for everything beside it. A document body that matches none of the prints the log holds for it is never taken in: when only another machine changed it, it waits up to an hour on this machine's clock for its history, and then you are asked. A document body that the log holds no print of at all, from before every body had one, is taken in with what this machine held set aside, since nothing answers for it either way. An attachment is held to the full digest the log records for it, by the sync round and by the window alike; one the log has no digest for is held only to the prefix its name carries. And tisty.toml, which names the store, is not signed: what it says of encryption is held to the signed log, as the encrypted cloud's section below explains. Someone who can write to the folder can also delete from it, and no signature brings back what was removed.

    What it does check besides is narrower and worth stating: it never writes through a symbolic link, into the folder or into any directory inside it; an attachment must hold the bytes its own name vouches for; one that was retired is not carried back in; a document body past the reader's ceiling is refused rather than swapped in for one that opens; a store carrying a different name stops everything until you decide; a place that asks for a later release than this one is neither read nor written; and a machine that was removed is refused outright — that removal is absorbing, so a removed identifier is never valid again and a machine that returns comes back as a new one.

  • A parcel of documents is a file somebody hands you, and it is treated as such. Every path inside the zip must be an ordinary relative path under docs/ or attachments/ — anything climbing out with .., an absolute path or a drive prefix is dropped rather than written. The names written in the manifest go through the same door as any other document name, so a manifest cannot point the reader at a file elsewhere on your disk. The manifest itself is read under a ceiling, and so is the parcel: bytes actually written are counted rather than the sizes the archive declares, and both the number of files and the number of entries in the manifest are capped. A folder name or an alias arriving inside is trimmed to the same limits the window applies, and an icon or a colour it does not recognise is dropped instead of stored. A parcel can say a folder was in the archive, and one it creates lands that way; what it cannot do is close a folder you already have, because joining by name leaves the folder that was already here deciding for itself. The same holds for a page put away on its own and for what an assistant marked: both travel, and both land as what they are, but the mark arrives without the machine that wrote it, because a parcel goes to other people.

    A parcel goes out signed once this computer has your identity. The key your root vouches for — the same one, under the same certificate, that signs what you share through Folio — signs an entry of its own, tisty-docs.signed.json, over the manifest's bytes exactly as written, the SHA-256 of every file under docs/ and attachments/, and the name you sign with, each part prefixed with its length. Nothing is canonicalised. Opening it, every file is hashed as it is written out, a name that comes twice breaks the signature, and the signature is checked against those bytes and against the keys your log says were taken back. Signed by the person this store belongs to, it lands as theirs, locked or not; signed by another root, as that person's writing, with their fingerprint; a signature that does not hold, a key taken back, or a file changed, added or missing makes it a stranger's. A signed name that hides a letter is never shown. One like the maker's under another root is left out of what the window says as the parcel lands, and marked as another key's where the document's details list who edited it. A signed parcel carries neither the store's name nor its seal, which would tie together every parcel the store wrote. A store that names no person yet asks before a signed parcel lands, and one opened anyway lands as the signer's for good.

    A signed parcel also carries, for each document that was shared, who edited it through Folio and which Folio document that was. Each entry is read again on arrival, kept only if it was signed for that document and its own signature holds, and stripped to its own fields, at most 64 of them. What the details show is the signer's word for the list, «as {name} tells it», beside each fingerprint; nothing here checks whether those keys were taken back since.

    Without a signature, a name in a parcel is a claim: anyone can edit the manifest in a zip. What an unsigned parcel can prove is which store wrote it. Every store keeps a secret apart from its name — the name travels in every parcel, the secret never leaves the machine, and it is kept outside the store so that nothing walking it, neither a sync nor a backup, can carry it off — and the manifest carries an HMAC of itself under it. So a parcel that arrives wearing your store's name, from somebody who read that name off a parcel you once handed them, lands as a stranger's: they cannot forge the seal, and neither claiming your name nor stripping the seal off gets them any further.

    Keeping it out of the copies has a price, and it is worth saying plainly. A backup carries the store's name and not what proves it, so a store restored on another machine cannot recognise the parcels it handed out before: they land as a stranger's writing. Taking Tisty off this machine leaves the secret where it is, beside your documents, so reinstalling here keeps that recognition. And a backup made before this was so still has the secret inside it, in the clear: the copies you already have are worth protecting like the documents they hold.

    The secret belongs to the store, not to the machine, and it is named after it: a machine that holds two stores holds two secrets, and neither can write over the other. That is also what makes starting over reversible — it mints a new name for the store, so the old secret simply stays under the old one, and the safety copy written moments before can be put back and recognised with nothing done by hand. A secret is never destroyed, only displaced: what a build has to move out of the way is kept under the hour it was replaced, and putting one back is renaming it. Leaving takes the diary and the settings, never any of this.

    The other side of that: a store opened with somebody else's settings — a data directory named on its own, without the configuration that goes with it — has its secret left exactly where it is. Nothing is moved out of a store that this install cannot account for.

    Locking keeps a parcel from whoever finds the file. Exporting everything to another machine of your own seals it with a number: XChaCha20-Poly1305 over 64 KiB blocks, under a key scrypt grinds out of that number, with each block's nonce carrying its count and a last-block byte, so a file cut short will not open as a whole one. The number does not decide whose it is: the signature does, and a locked parcel with no signature asks, once it opens, whether you locked it — yes lands it as yours, no as a stranger's — while what was somebody else's where it was packed stays theirs either way. Lose the number and the parcel is gone: nothing here can open it for you. A short number is a short number, and scrypt only makes each guess expensive rather than impossible.

An encrypted cloud, if you turn it on

Since 1.36, what Tisty keeps in your Google Drive, your OneDrive or your WebDAV server can be encrypted from Settings › Security. It is never offered for a folder kept in step by a desktop client. What follows is what holds it, and what it leaves in view.

One phrase, and keys that come from it. The phrase is 24 words from the BIP-39 lists, in English or in Spanish, and it is the whole of a 32-byte seed made from the system's randomness on your computer. From that seed, by HKDF-SHA256 under labels of their own, come the key of your cloud and the Ed25519 root of your identity; the log carries only the root's public half, as your fingerprint, and the certificates it signs for each computer. The seed is kept on the computer where it was made, in the system keychain on macOS and, on Windows, in a DPAPI-sealed folder apart from the cloud tokens, so the installer's uninstaller takes the tokens and leaves the phrase. It is never synced, backed up or put in a parcel. Encrypting asks that the phrase be confirmed first, by three of its words.

What seals the files. Each file is sealed with XChaCha20-Poly1305 over 64 KiB blocks, under a nonce that carries a random head for the file, the block's place and whether it is the last, so no block can be dropped, cut short or moved. The seal is bound to the cloud and to the file's name, so one sealed file cannot pass for another, and a file that does not open is refused rather than kept in part. A small mark, sealed/sealed.mark, sealed the same way, tells whether a phrase is this cloud's before anything comes down; the identifier it carries is tied to the cloud too, so two clouds' marks never show that they belong to one person.

What it does not hide. The names of the files, their sizes, when they change and how many computers you have stay in view; documents are named by an identifier, but an attachment's name is a lowercased form of its file's own. tisty.toml, moved.json, the store's identifier and the knock/ folder stay plain. Whatever the provider kept before you encrypted — its version history, its bin, the unencrypted copy until you delete it — is still there. A file too big for the provider to take encrypted is left out and stays as it was.

The provider cannot turn it off for you. The log, which every machine signs, says that the cloud is sealed and with which key, and only that key — or, on a computer whose log has not heard yet, the one that comes from the phrase the log names as yours — is used to read or to mark it. If tisty.toml stops saying the cloud is encrypted and none of your computers left a signed notice that it turned encryption off, Tisty writes nothing there unencrypted and tells you. That notice, knock/unsealed.json, is believed only from a machine the log seats, signed with the key the log knows for it, naming the key the log names, and later than the log's word that the cloud was sealed.

How the key reaches your other computers. It travels only inside the cloud, in the plain knock/ folder, and never as itself. A computer that needs it puts up an ask signed with its own Ed25519 key, carrying a one-use X25519 key that lives only in its memory. A computer that holds the cloud's key answers with that key sealed for the one-use key alone: XChaCha20-Poly1305 under a key drawn by HKDF-SHA256 from the X25519 exchange and bound to the ask. A replayed ask opens for nobody, since its one-use key is gone. Each file there is read to 4 KiB at most.

A computer already in your log gets it on its own. Its ask is answered only if it is signed with the key the log knows for that computer, and its answer is taken only from a computer the log seats with the key it knows, or one whose key your identity's root has certified. The mark sits with the provider, who could seal a mark of its own, so a mark that opens is never enough on its own.

A new computer is let in by comparing twenty digits. It shows a code drawn with scrypt from its ask, from the key of the computer that took the ask, and from a secret it committed to in the ask and reveals only after that computer took it — about 66 bits, so a provider standing in the middle can neither pick nor grind out a code that matches. You type the twenty digits on the computer letting it in, and that is what confirms the new computer's signing key there; then you say on the new computer that the other took them, and only then does it accept the answer. If you type nothing, nothing is handed over. An ask from a new computer lasts an hour.

Typing the phrase keeps only the cloud's key. On a computer that holds neither, you can type your 24 words instead. The mark is checked first, the identity signs for that computer once, and only the cloud's key is kept, in the keychain or the sealed folder; the phrase is not.

Nobody else can open it. There is no copy of the key outside your computers and the phrase. If you lose the phrase and every computer that holds the key, the encrypted cloud cannot be opened by you, by the provider or by this project.

Going back. Decrypt my cloud copies everything back unencrypted, leaves the signed notice so your other computers follow, lets go of a key it was handed, and deletes the encrypted copy. While a turn either way is under way, tisty.toml says so, and for its last steps the other computers wait rather than write; until the very last, cancelling loses nothing.

A shared document

Sharing by link goes through Folio: no new share starts until its signed limits say it is open. What follows is what holds it. What each step sends is in PRIVACY.md.

What seals it. A document is sealed on your computer with AES-256-GCM, under a key drawn by HKDF-SHA256 from 32 random bytes that live after the # of the link and never reach the server. Folio stores the file unopened. Its manifest stays in the clear, and that is all Folio can read of it.

Who wrote it. Every version is signed with the key of the computer that sent it, which your identity's root has certified. A sealed document names its owner in a signed block that stays through every version. An edit link adds a token that never reaches Folio: an editor signs the next version with their own key and that token, and the owner does not change. Each language of a document shared to edit has a token of its own, sealed inside the first language's signed map under a key drawn from that language's token, so one edit link opens them all and changing it changes them all. A language that does not name that document, or answers to another owner, stays in Folio.

What Tisty takes in. A document that arrives through a link is checked and opened on your computer, and what comes in is its body in Tisty's own language, never its HTML. One that does not hold is refused. Before anything is kept, Tisty shows who signed it and the fingerprint to compare, and warns if that key was taken back or cannot be confirmed. What the maker of Tisty signed, under the root that Folio's signed limits name, says «verified» beside the maker's name; another key whose alias is that name, or close to it, keeps its alias under a red line saying the name belongs to another key. A read link leaves a copy of your own, and nothing of it goes back.

What the limits can do. limits.json says whether sharing is open and what it allows. It is refused unless it is signed with minisign against a key fixed in the source, read to 16 KiB at most and following no redirect, and an older one never replaces a newer one. A note it carries is shown only if it is short and holds no character that can hide text. The terms you are asked to accept are checked against a print in that same signed file. Folio applies its own limits either way.

What this does not protect. The link is the key: whoever holds it reads the document, and stopping a share does not take back what they already read. The key and the tokens of each share sit in your log, as plain as the rest of it, and go wherever the log goes; an encrypted cloud seals them with everything else. Folio can tell that two documents come from one person, because the manifest carries your identity's public key. And a server that hides that a key was taken back cannot be caught by a reader on the web.

An assistant, while the door is open

Tisty speaks MCP so an assistant already running on your machine can file work for you. That door comes open on a new install and closes with one click; what follows is what holds it.

Admitting one is an event in your log, not a line in a settings file. Nothing arriving over the wire can register an agent; the log decides who may write, so an assistant cannot grant itself a voice by editing the file it can reach.

It writes under a device of its own. Throwing it out leaves everything it wrote in place. What it files is tagged, and it may only file into a list that already exists — it cannot make one.

It cannot reach what you hid. A task you folded away is neither returned nor counted; a count of one would say the thing exists. What it reads comes back without the absolute paths of your disk.

What it says about work being finished only ever adds. An assistant can mark a task as finished, with the account of what it did: one it filed, or one you wrote, where the mark is only its word for you to take or not. It cannot close anything, and the mark takes nothing out of your list: what is due today or overdue stays in the band you look for it in. Saying it twice over a mark you have not seen yet is refused, so a text it reads somewhere cannot fill your journal by repetition. Taking the mark off is yours alone — and doing so keeps the account, so you can see what it claimed and why you disagreed.

A body it did not read is a body it cannot replace. An assistant may write a whole document again, not only add to it — but only by sending back the print that document read at when it last looked. If you have written in it since, the write is refused and it has to read again. It can be wrong about a document; it cannot quietly take what you wrote while it was thinking. What it wrote over is kept beside the documents either way.

It can take one step back, and only when that step is the whole distance. That kept body is not only held, it is reachable: restore_doc puts it back, and what it replaces is kept in turn, so going back destroys nothing and going back twice leaves the document where it started. But not every write keeps a body — adding to the end does not, and your own saves from the window do not — so what is kept is not always one step behind. Kept alongside it is the print the document read at when it was set aside, and going back is refused unless the document still reads exactly that way. An assistant that wrote this morning does not undo an afternoon of yours by reaching for the wrong tool.

It can still do it on purpose: even_if_more goes back over writing done since, and an assistant that has read the document may decide that is what it wants. What that call writes over is kept in turn, so the same call again puts your afternoon back — and the answer says it went further than one write, which is the thing to look for if a document is suddenly shorter than you left it.

Attachments are the one thing that leaves the machine. They reach the shared folder, and from there whatever cloud client you run. So the file an assistant may copy is judged by its bytes rather than its name: pictures, PDFs, plain text and office documents pass; a private key, a PKCS#12 bundle or a file shaped like an environment file is refused even when renamed to .png. Files outside Downloads, Documents, Pictures, Desktop and the temporary folder are refused before anything is read, and the path is canonicalised first, so traversal does not reach past it.

And a file an assistant copies is one you pointed at, not one a document chose. Bringing a markdown file in copies what its text points at beside it, so the document does not break the day it moves — and «beside it» is the rule rather than a turn of phrase: only what sits in that file's own folder, or below it, is followed. A link that climbs out with .. is not fetched; its link is taken out, its words stay, and the answer names it. The five folders above are still the ceiling, but they are no longer the fence: the text of a markdown file somebody sent you cannot pick which of your own files are copied into a store that syncs.

What this does not protect against. An assistant under prompt injection is still an assistant with your permission: it can file nonsense, write documents you did not ask for, write over one you already had with a version of its own, put back over a document a body from before you wrote in it — which it has to ask for, and which is kept and can be put back — and put a screenshot holding a password into a task, because a screenshot is a picture and Tisty cannot read what is in it. Anything it reads travels wherever that assistant travels — that is between it and you, and it is why closing the door is one click.

Supported versions

Until the first stable release, only the latest published version receives fixes.

There aren't any published security advisories