Vibecode PastePal
track this build4 phases, 12 steps, beginner friendly0%The core loop is small enough for a capable coding agent to produce a useful local version in one sitting. For PastePal, keep a local clipboard history with collections, search, and sensitive-item rules. The hard boundary is native apple apps, icloud sync, rich content support, and polish, plus os integration polish and maintenance.
You are building a lean indie version of PastePal. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # PastePal · indie build A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | A platform clipboard observer honouring the sensitive-content marker | applications signal what should not be recorded and ignoring that is the first failure | | Exclusion | Source application and content shape rules applied before writing | filtering at display leaves it on disk | | Storage | Encrypted local database with a bounded history | an unbounded clipboard log is a growing liability | | Search | Full text over decrypted content in memory | a plaintext index would defeat the encryption | | Runtime | A local application with a global hotkey | the value is being faster than switching windows | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Platform clipboard access including the sensitivity marker** · free - Why: The marker is how a password manager tells you not to record something, and reading it is platform-specific and easy to miss entirely. - Get it: Your platform's clipboard interface, and find the marker's name in its documentation before writing the capture path. - Verify: Content copied from a password manager is identifiable as marked - [ ] **Platform keychain access** · free - Why: The database encryption key has to live somewhere better than beside the database. - Get it: Your platform's credential store interface. - Verify: A key stores and retrieves and is not readable from a plain file - [ ] **Your exclusion list, written before capture** · free - Why: Deciding what never to record after a month of recording is deciding it too late, since the data already exists. - Get it: List applications to exclude and the content shapes that must never be stored, erring wide. - Verify: The list is loaded by the capture path before the first run - [ ] **A password manager to test against** · free - Why: The single most important behaviour is not recording what a password manager copies, and that is only verifiable against a real one. - Get it: Any password manager that sets the sensitivity marker. - Verify: Copying a password from it produces no stored item ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. - native Apple apps, iCloud sync, rich content support, and polish - years of OS-version edge cases - large extension ecosystem - cross-device sync - commercial support and rapid compatibility fixes If one of those is essential to you, that is the reason to keep paying for PastePal, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · PastePal The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a clipboard history that refuses to keep the things it should not to replace PastePal. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero A clipboard history captures everything you copy, and what people copy includes passwords, recovery codes, card numbers and authentication tokens. A tool that logs all of it to disk has quietly created the most valuable file on the machine, and it did so as a side effect of a convenience feature. So the exclusions come first, before the history exists. Respect the marker that password managers and other applications set to say do not record this. Exclude by source application. Detect the shapes of card numbers, private keys and tokens and never write them. Encrypt what remains at rest. The order matters: exclusion happens before the write, not as a filter when displaying. Something filtered at display is still on disk, still in the backup and still in a crash report, and the only version of this that is defensible is the one where the sensitive value never reached storage at all. ### Stack (fixed, do not substitute) - Capture: A platform clipboard observer honouring the sensitive-content marker · applications signal what should not be recorded and ignoring that is the first failure - Exclusion: Source application and content shape rules applied before writing · filtering at display leaves it on disk - Storage: Encrypted local database with a bounded history · an unbounded clipboard log is a growing liability - Search: Full text over decrypted content in memory · a plaintext index would defeat the encryption - Runtime: A local application with a global hotkey · the value is being faster than switching windows ### Data model (create this before Phase 1) - items(id, kind, content, source_app, copied_at, pinned, collection_id) · kind is text, image, file_ref or rich - collections(id, name, ordinal) · saved snippets, kept indefinitely unlike history - exclusions(id, rule_kind, pattern, created_at) · rule_kind is app, shape or marker - skipped(at, reason, source_app) · that something was excluded, never what it was - settings(key, value) · hotkey, retention, encryption state - The skipped table records that an exclusion fired and why, and deliberately never records the content it excluded, because that record would be exactly the thing being protected. ### Phase 1 · Not capturing what must not be captured Build: Observe the clipboard, honour the platform marker that flags content as not to be recorded, exclude configured source applications, and detect card numbers, private keys and token shapes. Apply all of it before writing anything. Trap: Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. Done when: Content marked sensitive by the source application produces no stored item, verified by copying from a password manager and inspecting the database directly. An excluded application produces no rows. A card number and a private key pasted from any application are not stored. The skipped record notes the reason and source without the content. Do not build yet: No history, search, collections or sync. ### Phase 2 · A history that is faster than not having one Build: A bounded history with a global hotkey opening a searchable list, keyboard navigable and closing on selection. Measure the time from hotkey to pasted. Preserve the original format including rich text and images. Trap: A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. Done when: Hotkey to pasted is measured at under a second including the first use after a cold start. The list is fully keyboard navigable and closes on selection. Rich text, images and file references paste in their original format. History is bounded and the oldest items are removed rather than growing indefinitely. Do not build yet: No search, collections or encryption yet. ### Phase 3 · Encrypted at rest, searchable in memory Build: Encrypt the database at rest with a key from the platform keychain. Search over decrypted content in memory without writing any index. Lock on idle so the decrypted content is not resident indefinitely. Trap: Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. Done when: The database is unreadable without the key, verified by opening it directly. No plaintext index exists on disk, asserted in the suite. Search across the full bounded history returns as you type. The application locks on idle and decrypted content is cleared from memory when it does. Do not build yet: No collections or sync yet. ### Phase 4 · Things worth keeping Build: Collections of saved items kept indefinitely, distinct from history retention. Pin an item from history into a collection. Apply the same exclusion rules to collections, so a sensitive item cannot be saved deliberately either. Trap: Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. Done when: Collections persist beyond history retention. An item matching an exclusion rule cannot be pinned into a collection, with the reason shown. Collections are searchable alongside history. Reordering and organising collections works without a mouse. Do not build yet: No sync or export yet. ### Phase 5 · Sync, export and forgetting Build: Optional sync of collections only, never history, through storage the user controls. Export collections in a readable format. A clear action that destroys history immediately including any cached copy. Trap: Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. Done when: Only collections sync and history never leaves the machine, verified by inspecting what is transmitted. A clear action destroys history immediately including any in-memory copy, verified after clearing. Export contains collections in a readable format with an explicit plaintext warning. Sync is optional and single-machine use is complete. Do not build yet: Nothing after this. ### Out of scope (and why) - Syncing rolling history across devices, which multiplies the exposure of transient content for very little benefit. - Optical recognition of copied images, which would extract text from screenshots that may contain anything. - Cloud storage of any kind, which turns a local convenience into a hosted repository of everything you copied. - Cross-application automation driven by clipboard content, which is a different tool with a much larger surface. ### README must contain - That exclusions are applied in the capture path, so sensitive content never reaches disk - That the platform sensitivity marker set by password managers is honoured - That the skipped record notes the reason and never the content - That search runs in memory because a plaintext index would make the encryption decorative - That only deliberately saved collections sync, and rolling history never leaves the machine ===== AGENTS.md ===== # Agent instructions · PastePal indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A platform clipboard observer honouring the sensitive-content marker, Source application and content shape rules applied before writing, Encrypted local database with a bounded history, Full text over decrypted content in memory, A local application with a global hotkey. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ===== BUILD_PLAN.md ===== # Build plan · PastePal A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Not capturing what must not be captured Sensitive content never reaches disk in the first place. ### Steps 1. Honour the platform sensitivity marker Password managers set it. Ignoring it is the first failure. Files: `src/capture.ts` 2. Apply shape rules before writing Card numbers, keys and tokens, checked in the capture path. Files: `src/exclusions.ts`, `tests/exclusions.test.ts` 3. Record that something was skipped, not what The record would otherwise be the thing you were protecting. Files: `src/skipped.ts` ### Done when - [ ] Marked content produces no database row - [ ] Excluded applications produce no rows - [ ] Card numbers and keys are never stored - [ ] Skipped records contain no content ### Watch out - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. ## Phase 2 · A history that is faster than not having one Reaching an item from three copies ago takes under a second. ### Steps 1. Pre-warm the window The first use of the day is the one that decides the habit. Files: `src/window.ts` 2. Preserve original formats A rich text item pasted as plain text is a lost item. Files: `src/formats.ts` 3. Bound the history An unbounded clipboard log is a growing liability. Files: `src/retention.ts` ### Done when - [ ] Hotkey to pasted is under a second including cold start - [ ] The list is fully keyboard navigable - [ ] Rich text, images and files paste in original format - [ ] History is bounded and prunes oldest ### Watch out - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. ## Phase 3 · Encrypted at rest, searchable in memory The stored history is unreadable without the key, and still searchable. ### Steps 1. Encrypt with a key from the keychain So the key is not sitting beside the database. Files: `src/crypto.ts` 2. Search in memory, index nothing A plaintext index makes the encryption decorative. Files: `src/search.ts`, `tests/no-index.test.ts` 3. Clear memory on lock Decrypted history resident indefinitely defeats the lock. Files: `src/lock.ts` ### Done when - [ ] The database is unreadable without the key - [ ] No plaintext index exists on disk - [ ] Search is instant across the bounded history - [ ] Locking clears decrypted content from memory ### Watch out - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. ## Phase 4 · Things worth keeping Snippets you use often are saved separately from the rolling history. ### Steps 1. Keep collections outside retention A saved snippet should not roll out of history. Files: `src/collections.ts` 2. Apply exclusions to pinning too Otherwise the sensitive item persists indefinitely rather than briefly. Files: `src/collections.ts` 3. Keep organising keyboard driven The whole tool exists to avoid reaching for a mouse. Files: `src/collections.ts` ### Done when - [ ] Collections persist beyond retention - [ ] Excluded content cannot be pinned, with a reason - [ ] Collections search alongside history - [ ] Organising works without a mouse ### Watch out - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ## Not in this build - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. ## After v1, if you want it - Per-application paste formatting, since pasting into a terminal and a document want different things - A quick transform on paste such as trimming or case changes, which is the next most common need - Smarter shape detection tuned from your own skipped log, which needs no content to improve ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Encrypted history and collections. DB_PATH=~/.local/share/pastepal/history.db # Required · secret. The encryption key lives in the platform keychain, not beside the database. DB_KEY_REF=a keychain item reference # Required. Bounded. An unbounded clipboard log is a growing liability. HISTORY_LIMIT=200 # Required. Applied in the capture path, before anything is written. EXCLUSIONS_PATH=./config/exclusions.txt # Required. Present as a constant. It is how a password manager says do not record this. HONOUR_SENSITIVITY_MARKER=always # Required. Present as a constant. Only deliberately saved collections sync. SYNC_HISTORY=never
You are building a lean indie version of PastePal. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # PastePal · indie build A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | A platform clipboard observer honouring the sensitive-content marker | applications signal what should not be recorded and ignoring that is the first failure | | Exclusion | Source application and content shape rules applied before writing | filtering at display leaves it on disk | | Storage | Encrypted local database with a bounded history | an unbounded clipboard log is a growing liability | | Search | Full text over decrypted content in memory | a plaintext index would defeat the encryption | | Runtime | A local application with a global hotkey | the value is being faster than switching windows | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Platform clipboard access including the sensitivity marker** · free - Why: The marker is how a password manager tells you not to record something, and reading it is platform-specific and easy to miss entirely. - Get it: Your platform's clipboard interface, and find the marker's name in its documentation before writing the capture path. - Verify: Content copied from a password manager is identifiable as marked - [ ] **Platform keychain access** · free - Why: The database encryption key has to live somewhere better than beside the database. - Get it: Your platform's credential store interface. - Verify: A key stores and retrieves and is not readable from a plain file - [ ] **Your exclusion list, written before capture** · free - Why: Deciding what never to record after a month of recording is deciding it too late, since the data already exists. - Get it: List applications to exclude and the content shapes that must never be stored, erring wide. - Verify: The list is loaded by the capture path before the first run - [ ] **A password manager to test against** · free - Why: The single most important behaviour is not recording what a password manager copies, and that is only verifiable against a real one. - Get it: Any password manager that sets the sensitivity marker. - Verify: Copying a password from it produces no stored item ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. - native Apple apps, iCloud sync, rich content support, and polish - years of OS-version edge cases - large extension ecosystem - cross-device sync - commercial support and rapid compatibility fixes If one of those is essential to you, that is the reason to keep paying for PastePal, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · PastePal The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a clipboard history that refuses to keep the things it should not to replace PastePal. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero A clipboard history captures everything you copy, and what people copy includes passwords, recovery codes, card numbers and authentication tokens. A tool that logs all of it to disk has quietly created the most valuable file on the machine, and it did so as a side effect of a convenience feature. So the exclusions come first, before the history exists. Respect the marker that password managers and other applications set to say do not record this. Exclude by source application. Detect the shapes of card numbers, private keys and tokens and never write them. Encrypt what remains at rest. The order matters: exclusion happens before the write, not as a filter when displaying. Something filtered at display is still on disk, still in the backup and still in a crash report, and the only version of this that is defensible is the one where the sensitive value never reached storage at all. ### Stack (fixed, do not substitute) - Capture: A platform clipboard observer honouring the sensitive-content marker · applications signal what should not be recorded and ignoring that is the first failure - Exclusion: Source application and content shape rules applied before writing · filtering at display leaves it on disk - Storage: Encrypted local database with a bounded history · an unbounded clipboard log is a growing liability - Search: Full text over decrypted content in memory · a plaintext index would defeat the encryption - Runtime: A local application with a global hotkey · the value is being faster than switching windows ### Data model (create this before Phase 1) - items(id, kind, content, source_app, copied_at, pinned, collection_id) · kind is text, image, file_ref or rich - collections(id, name, ordinal) · saved snippets, kept indefinitely unlike history - exclusions(id, rule_kind, pattern, created_at) · rule_kind is app, shape or marker - skipped(at, reason, source_app) · that something was excluded, never what it was - settings(key, value) · hotkey, retention, encryption state - The skipped table records that an exclusion fired and why, and deliberately never records the content it excluded, because that record would be exactly the thing being protected. ### Phase 1 · Not capturing what must not be captured Build: Observe the clipboard, honour the platform marker that flags content as not to be recorded, exclude configured source applications, and detect card numbers, private keys and token shapes. Apply all of it before writing anything. Trap: Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. Done when: Content marked sensitive by the source application produces no stored item, verified by copying from a password manager and inspecting the database directly. An excluded application produces no rows. A card number and a private key pasted from any application are not stored. The skipped record notes the reason and source without the content. Do not build yet: No history, search, collections or sync. ### Phase 2 · A history that is faster than not having one Build: A bounded history with a global hotkey opening a searchable list, keyboard navigable and closing on selection. Measure the time from hotkey to pasted. Preserve the original format including rich text and images. Trap: A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. Done when: Hotkey to pasted is measured at under a second including the first use after a cold start. The list is fully keyboard navigable and closes on selection. Rich text, images and file references paste in their original format. History is bounded and the oldest items are removed rather than growing indefinitely. Do not build yet: No search, collections or encryption yet. ### Phase 3 · Encrypted at rest, searchable in memory Build: Encrypt the database at rest with a key from the platform keychain. Search over decrypted content in memory without writing any index. Lock on idle so the decrypted content is not resident indefinitely. Trap: Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. Done when: The database is unreadable without the key, verified by opening it directly. No plaintext index exists on disk, asserted in the suite. Search across the full bounded history returns as you type. The application locks on idle and decrypted content is cleared from memory when it does. Do not build yet: No collections or sync yet. ### Phase 4 · Things worth keeping Build: Collections of saved items kept indefinitely, distinct from history retention. Pin an item from history into a collection. Apply the same exclusion rules to collections, so a sensitive item cannot be saved deliberately either. Trap: Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. Done when: Collections persist beyond history retention. An item matching an exclusion rule cannot be pinned into a collection, with the reason shown. Collections are searchable alongside history. Reordering and organising collections works without a mouse. Do not build yet: No sync or export yet. ### Phase 5 · Sync, export and forgetting Build: Optional sync of collections only, never history, through storage the user controls. Export collections in a readable format. A clear action that destroys history immediately including any cached copy. Trap: Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. Done when: Only collections sync and history never leaves the machine, verified by inspecting what is transmitted. A clear action destroys history immediately including any in-memory copy, verified after clearing. Export contains collections in a readable format with an explicit plaintext warning. Sync is optional and single-machine use is complete. Do not build yet: Nothing after this. ### Out of scope (and why) - Syncing rolling history across devices, which multiplies the exposure of transient content for very little benefit. - Optical recognition of copied images, which would extract text from screenshots that may contain anything. - Cloud storage of any kind, which turns a local convenience into a hosted repository of everything you copied. - Cross-application automation driven by clipboard content, which is a different tool with a much larger surface. ### README must contain - That exclusions are applied in the capture path, so sensitive content never reaches disk - That the platform sensitivity marker set by password managers is honoured - That the skipped record notes the reason and never the content - That search runs in memory because a plaintext index would make the encryption decorative - That only deliberately saved collections sync, and rolling history never leaves the machine ===== AGENTS.md ===== # Agent instructions · PastePal indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A platform clipboard observer honouring the sensitive-content marker, Source application and content shape rules applied before writing, Encrypted local database with a bounded history, Full text over decrypted content in memory, A local application with a global hotkey. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ===== BUILD_PLAN.md ===== # Build plan · PastePal A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Not capturing what must not be captured Sensitive content never reaches disk in the first place. ### Steps 1. Honour the platform sensitivity marker Password managers set it. Ignoring it is the first failure. Files: `src/capture.ts` 2. Apply shape rules before writing Card numbers, keys and tokens, checked in the capture path. Files: `src/exclusions.ts`, `tests/exclusions.test.ts` 3. Record that something was skipped, not what The record would otherwise be the thing you were protecting. Files: `src/skipped.ts` ### Done when - [ ] Marked content produces no database row - [ ] Excluded applications produce no rows - [ ] Card numbers and keys are never stored - [ ] Skipped records contain no content ### Watch out - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. ## Phase 2 · A history that is faster than not having one Reaching an item from three copies ago takes under a second. ### Steps 1. Pre-warm the window The first use of the day is the one that decides the habit. Files: `src/window.ts` 2. Preserve original formats A rich text item pasted as plain text is a lost item. Files: `src/formats.ts` 3. Bound the history An unbounded clipboard log is a growing liability. Files: `src/retention.ts` ### Done when - [ ] Hotkey to pasted is under a second including cold start - [ ] The list is fully keyboard navigable - [ ] Rich text, images and files paste in original format - [ ] History is bounded and prunes oldest ### Watch out - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. ## Phase 3 · Encrypted at rest, searchable in memory The stored history is unreadable without the key, and still searchable. ### Steps 1. Encrypt with a key from the keychain So the key is not sitting beside the database. Files: `src/crypto.ts` 2. Search in memory, index nothing A plaintext index makes the encryption decorative. Files: `src/search.ts`, `tests/no-index.test.ts` 3. Clear memory on lock Decrypted history resident indefinitely defeats the lock. Files: `src/lock.ts` ### Done when - [ ] The database is unreadable without the key - [ ] No plaintext index exists on disk - [ ] Search is instant across the bounded history - [ ] Locking clears decrypted content from memory ### Watch out - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. ## Phase 4 · Things worth keeping Snippets you use often are saved separately from the rolling history. ### Steps 1. Keep collections outside retention A saved snippet should not roll out of history. Files: `src/collections.ts` 2. Apply exclusions to pinning too Otherwise the sensitive item persists indefinitely rather than briefly. Files: `src/collections.ts` 3. Keep organising keyboard driven The whole tool exists to avoid reaching for a mouse. Files: `src/collections.ts` ### Done when - [ ] Collections persist beyond retention - [ ] Excluded content cannot be pinned, with a reason - [ ] Collections search alongside history - [ ] Organising works without a mouse ### Watch out - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ## Not in this build - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. ## After v1, if you want it - Per-application paste formatting, since pasting into a terminal and a document want different things - A quick transform on paste such as trimming or case changes, which is the next most common need - Smarter shape detection tuned from your own skipped log, which needs no content to improve ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Encrypted history and collections. DB_PATH=~/.local/share/pastepal/history.db # Required · secret. The encryption key lives in the platform keychain, not beside the database. DB_KEY_REF=a keychain item reference # Required. Bounded. An unbounded clipboard log is a growing liability. HISTORY_LIMIT=200 # Required. Applied in the capture path, before anything is written. EXCLUSIONS_PATH=./config/exclusions.txt # Required. Present as a constant. It is how a password manager says do not record this. HONOUR_SENSITIVITY_MARKER=always # Required. Present as a constant. Only deliberately saved collections sync. SYNC_HISTORY=never
You are building a production product version of PastePal. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # PastePal · product brief ## Problem The core loop is small enough for a capable coding agent to produce a useful local version in one sitting. For PastePal, keep a local clipboard history with collections, search, and sensitive-item rules. The hard boundary is native apple apps, icloud sync, rich content support, and polish, plus os integration polish and maintenance. ## Product outcome A clipboard history fast enough to change how you work, that never wrote down the password you copied five minutes ago. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS 14 or later - Xcode - only the permissions required by the selected feature ## Explicit non-goals for v1 - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. - native Apple apps, iCloud sync, rich content support, and polish - years of OS-version edge cases - large extension ecosystem - cross-device sync - commercial support and rapid compatibility fixes ## Success criteria - Content marked sensitive is confirmed to produce no row - Card numbers and keys are confirmed never stored - No plaintext index is confirmed on disk - Excluded content is confirmed unpinnable - History is confirmed never to leave the machine ===== BRIEF.md ===== # Build brief · PastePal The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a clipboard history that refuses to keep the things it should not to replace PastePal. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero A clipboard history captures everything you copy, and what people copy includes passwords, recovery codes, card numbers and authentication tokens. A tool that logs all of it to disk has quietly created the most valuable file on the machine, and it did so as a side effect of a convenience feature. So the exclusions come first, before the history exists. Respect the marker that password managers and other applications set to say do not record this. Exclude by source application. Detect the shapes of card numbers, private keys and tokens and never write them. Encrypt what remains at rest. The order matters: exclusion happens before the write, not as a filter when displaying. Something filtered at display is still on disk, still in the backup and still in a crash report, and the only version of this that is defensible is the one where the sensitive value never reached storage at all. ### Stack (fixed, do not substitute) - Capture: A platform clipboard observer honouring the sensitive-content marker · applications signal what should not be recorded and ignoring that is the first failure - Exclusion: Source application and content shape rules applied before writing · filtering at display leaves it on disk - Storage: Encrypted local database with a bounded history · an unbounded clipboard log is a growing liability - Search: Full text over decrypted content in memory · a plaintext index would defeat the encryption - Runtime: A local application with a global hotkey · the value is being faster than switching windows ### Data model (create this before Phase 1) - items(id, kind, content, source_app, copied_at, pinned, collection_id) · kind is text, image, file_ref or rich - collections(id, name, ordinal) · saved snippets, kept indefinitely unlike history - exclusions(id, rule_kind, pattern, created_at) · rule_kind is app, shape or marker - skipped(at, reason, source_app) · that something was excluded, never what it was - settings(key, value) · hotkey, retention, encryption state - The skipped table records that an exclusion fired and why, and deliberately never records the content it excluded, because that record would be exactly the thing being protected. ### Phase 1 · Not capturing what must not be captured Build: Observe the clipboard, honour the platform marker that flags content as not to be recorded, exclude configured source applications, and detect card numbers, private keys and token shapes. Apply all of it before writing anything. Trap: Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. Done when: Content marked sensitive by the source application produces no stored item, verified by copying from a password manager and inspecting the database directly. An excluded application produces no rows. A card number and a private key pasted from any application are not stored. The skipped record notes the reason and source without the content. Do not build yet: No history, search, collections or sync. ### Phase 2 · A history that is faster than not having one Build: A bounded history with a global hotkey opening a searchable list, keyboard navigable and closing on selection. Measure the time from hotkey to pasted. Preserve the original format including rich text and images. Trap: A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. Done when: Hotkey to pasted is measured at under a second including the first use after a cold start. The list is fully keyboard navigable and closes on selection. Rich text, images and file references paste in their original format. History is bounded and the oldest items are removed rather than growing indefinitely. Do not build yet: No search, collections or encryption yet. ### Phase 3 · Encrypted at rest, searchable in memory Build: Encrypt the database at rest with a key from the platform keychain. Search over decrypted content in memory without writing any index. Lock on idle so the decrypted content is not resident indefinitely. Trap: Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. Done when: The database is unreadable without the key, verified by opening it directly. No plaintext index exists on disk, asserted in the suite. Search across the full bounded history returns as you type. The application locks on idle and decrypted content is cleared from memory when it does. Do not build yet: No collections or sync yet. ### Phase 4 · Things worth keeping Build: Collections of saved items kept indefinitely, distinct from history retention. Pin an item from history into a collection. Apply the same exclusion rules to collections, so a sensitive item cannot be saved deliberately either. Trap: Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. Done when: Collections persist beyond history retention. An item matching an exclusion rule cannot be pinned into a collection, with the reason shown. Collections are searchable alongside history. Reordering and organising collections works without a mouse. Do not build yet: No sync or export yet. ### Phase 5 · Sync, export and forgetting Build: Optional sync of collections only, never history, through storage the user controls. Export collections in a readable format. A clear action that destroys history immediately including any cached copy. Trap: Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. Done when: Only collections sync and history never leaves the machine, verified by inspecting what is transmitted. A clear action destroys history immediately including any in-memory copy, verified after clearing. Export contains collections in a readable format with an explicit plaintext warning. Sync is optional and single-machine use is complete. Do not build yet: Nothing after this. ### Out of scope (and why) - Syncing rolling history across devices, which multiplies the exposure of transient content for very little benefit. - Optical recognition of copied images, which would extract text from screenshots that may contain anything. - Cloud storage of any kind, which turns a local convenience into a hosted repository of everything you copied. - Cross-application automation driven by clipboard content, which is a different tool with a much larger surface. ### README must contain - That exclusions are applied in the capture path, so sensitive content never reaches disk - That the platform sensitivity marker set by password managers is honoured - That the skipped record notes the reason and never the content - That search runs in memory because a plaintext index would make the encryption decorative - That only deliberately saved collections sync, and rolling history never leaves the machine ===== ARCHITECTURE.md ===== # Architecture · PastePal ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | A platform clipboard observer honouring the sensitive-content marker | applications signal what should not be recorded and ignoring that is the first failure | | Exclusion | Source application and content shape rules applied before writing | filtering at display leaves it on disk | | Storage | Encrypted local database with a bounded history | an unbounded clipboard log is a growing liability | | Search | Full text over decrypted content in memory | a plaintext index would defeat the encryption | | Runtime | A local application with a global hotkey | the value is being faster than switching windows | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Observing the clipboard and honouring the marker. | Platform specific. Ignoring the marker is the first and worst failure. | | Exclusions | Rules applied before writing. | Not swappable for display filtering, which leaves everything on disk. | | Storage | Encrypted bounded history. | Any store, provided no plaintext index accompanies it. | | Collections | Deliberately saved items under the same rules. | Any implementation, provided exclusions apply to pinning too. | | Sync | Collections only, never history. | Any transport, provided rolling history stays local. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Encrypted history and collections. - `DB_KEY_REF` · required, secret · The encryption key lives in the platform keychain, not beside the database. - `HISTORY_LIMIT` · required · Bounded. An unbounded clipboard log is a growing liability. - `EXCLUSIONS_PATH` · required · Applied in the capture path, before anything is written. - `HONOUR_SENSITIVITY_MARKER` · required · Present as a constant. It is how a password manager says do not record this. - `SYNC_HISTORY` · required · Present as a constant. Only deliberately saved collections sync. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · PastePal product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: A platform clipboard observer honouring the sensitive-content marker, Source application and content shape rules applied before writing, Encrypted local database with a bounded history, Full text over decrypted content in memory, A local application with a global hotkey. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. - Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. ===== MILESTONES.md ===== # Delivery milestones · PastePal Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Not capturing what must not be captured Sensitive content never reaches disk in the first place. ### Steps 1. Honour the platform sensitivity marker Password managers set it. Ignoring it is the first failure. Files: `src/capture.ts` 2. Apply shape rules before writing Card numbers, keys and tokens, checked in the capture path. Files: `src/exclusions.ts`, `tests/exclusions.test.ts` 3. Record that something was skipped, not what The record would otherwise be the thing you were protecting. Files: `src/skipped.ts` ### Done when - [ ] Marked content produces no database row - [ ] Excluded applications produce no rows - [ ] Card numbers and keys are never stored - [ ] Skipped records contain no content ### Watch out - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. ## M2 · A history that is faster than not having one Reaching an item from three copies ago takes under a second. ### Steps 1. Pre-warm the window The first use of the day is the one that decides the habit. Files: `src/window.ts` 2. Preserve original formats A rich text item pasted as plain text is a lost item. Files: `src/formats.ts` 3. Bound the history An unbounded clipboard log is a growing liability. Files: `src/retention.ts` ### Done when - [ ] Hotkey to pasted is under a second including cold start - [ ] The list is fully keyboard navigable - [ ] Rich text, images and files paste in original format - [ ] History is bounded and prunes oldest ### Watch out - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. ## M3 · Encrypted at rest, searchable in memory The stored history is unreadable without the key, and still searchable. ### Steps 1. Encrypt with a key from the keychain So the key is not sitting beside the database. Files: `src/crypto.ts` 2. Search in memory, index nothing A plaintext index makes the encryption decorative. Files: `src/search.ts`, `tests/no-index.test.ts` 3. Clear memory on lock Decrypted history resident indefinitely defeats the lock. Files: `src/lock.ts` ### Done when - [ ] The database is unreadable without the key - [ ] No plaintext index exists on disk - [ ] Search is instant across the bounded history - [ ] Locking clears decrypted content from memory ### Watch out - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. ## M4 · Things worth keeping Snippets you use often are saved separately from the rolling history. ### Steps 1. Keep collections outside retention A saved snippet should not roll out of history. Files: `src/collections.ts` 2. Apply exclusions to pinning too Otherwise the sensitive item persists indefinitely rather than briefly. Files: `src/collections.ts` 3. Keep organising keyboard driven The whole tool exists to avoid reaching for a mouse. Files: `src/collections.ts` ### Done when - [ ] Collections persist beyond retention - [ ] Excluded content cannot be pinned, with a reason - [ ] Collections search alongside history - [ ] Organising works without a mouse ### Watch out - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ## M5 · Sync, export and forgetting (production only) History can move between machines or be destroyed completely. ### Steps 1. Sync collections only History is transient by definition and multiplying it is pure exposure. Files: `src/sync.ts` 2. Make clearing immediate and total Including memory, not only the database. Files: `src/clear.ts` 3. Warn plainly on export An exported collection file is plaintext by nature. Files: `src/export.ts` ### Done when - [ ] History never leaves the machine - [ ] Clearing destroys history including memory - [ ] Export warns that the file is plaintext - [ ] Single-machine use is complete without sync ### Watch out - Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. ===== OPERATIONS.md ===== # Operations · PastePal ## Backup Collections are worth backing up and history is not, which is convenient because history is the sensitive half. Back up collections and let history be genuinely transient. ## Restore Restore collections and confirm no history came back, since a restored history reintroduces content the retention had already discarded. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a personal tool the check is that exclusions are firing at all. A skipped count of zero over a week almost certainly means the marker is not being read rather than that nothing sensitive was copied. ## Incident checklist A password appearing in the history is the failure, and it means either the marker was not honoured or a shape rule was missing. The skipped log tells you which without telling you what. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Content marked sensitive is confirmed to produce no row - [ ] Card numbers and keys are confirmed never stored - [ ] No plaintext index is confirmed on disk - [ ] Excluded content is confirmed unpinnable - [ ] History is confirmed never to leave the machine ## Launch constraint Do not market omitted PastePal capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Encrypted history and collections. DB_PATH=~/.local/share/pastepal/history.db # Required · secret. The encryption key lives in the platform keychain, not beside the database. DB_KEY_REF=a keychain item reference # Required. Bounded. An unbounded clipboard log is a growing liability. HISTORY_LIMIT=200 # Required. Applied in the capture path, before anything is written. EXCLUSIONS_PATH=./config/exclusions.txt # Required. Present as a constant. It is how a password manager says do not record this. HONOUR_SENSITIVITY_MARKER=always # Required. Present as a constant. Only deliberately saved collections sync. SYNC_HISTORY=never
# PastePal · indie build A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | A platform clipboard observer honouring the sensitive-content marker | applications signal what should not be recorded and ignoring that is the first failure | | Exclusion | Source application and content shape rules applied before writing | filtering at display leaves it on disk | | Storage | Encrypted local database with a bounded history | an unbounded clipboard log is a growing liability | | Search | Full text over decrypted content in memory | a plaintext index would defeat the encryption | | Runtime | A local application with a global hotkey | the value is being faster than switching windows | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Platform clipboard access including the sensitivity marker** · free - Why: The marker is how a password manager tells you not to record something, and reading it is platform-specific and easy to miss entirely. - Get it: Your platform's clipboard interface, and find the marker's name in its documentation before writing the capture path. - Verify: Content copied from a password manager is identifiable as marked - [ ] **Platform keychain access** · free - Why: The database encryption key has to live somewhere better than beside the database. - Get it: Your platform's credential store interface. - Verify: A key stores and retrieves and is not readable from a plain file - [ ] **Your exclusion list, written before capture** · free - Why: Deciding what never to record after a month of recording is deciding it too late, since the data already exists. - Get it: List applications to exclude and the content shapes that must never be stored, erring wide. - Verify: The list is loaded by the capture path before the first run - [ ] **A password manager to test against** · free - Why: The single most important behaviour is not recording what a password manager copies, and that is only verifiable against a real one. - Get it: Any password manager that sets the sensitivity marker. - Verify: Copying a password from it produces no stored item ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. - native Apple apps, iCloud sync, rich content support, and polish - years of OS-version edge cases - large extension ecosystem - cross-device sync - commercial support and rapid compatibility fixes If one of those is essential to you, that is the reason to keep paying for PastePal, and the README should say so rather than pretend.
# Build brief · PastePal The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a clipboard history that refuses to keep the things it should not to replace PastePal. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero A clipboard history captures everything you copy, and what people copy includes passwords, recovery codes, card numbers and authentication tokens. A tool that logs all of it to disk has quietly created the most valuable file on the machine, and it did so as a side effect of a convenience feature. So the exclusions come first, before the history exists. Respect the marker that password managers and other applications set to say do not record this. Exclude by source application. Detect the shapes of card numbers, private keys and tokens and never write them. Encrypt what remains at rest. The order matters: exclusion happens before the write, not as a filter when displaying. Something filtered at display is still on disk, still in the backup and still in a crash report, and the only version of this that is defensible is the one where the sensitive value never reached storage at all. ### Stack (fixed, do not substitute) - Capture: A platform clipboard observer honouring the sensitive-content marker · applications signal what should not be recorded and ignoring that is the first failure - Exclusion: Source application and content shape rules applied before writing · filtering at display leaves it on disk - Storage: Encrypted local database with a bounded history · an unbounded clipboard log is a growing liability - Search: Full text over decrypted content in memory · a plaintext index would defeat the encryption - Runtime: A local application with a global hotkey · the value is being faster than switching windows ### Data model (create this before Phase 1) - items(id, kind, content, source_app, copied_at, pinned, collection_id) · kind is text, image, file_ref or rich - collections(id, name, ordinal) · saved snippets, kept indefinitely unlike history - exclusions(id, rule_kind, pattern, created_at) · rule_kind is app, shape or marker - skipped(at, reason, source_app) · that something was excluded, never what it was - settings(key, value) · hotkey, retention, encryption state - The skipped table records that an exclusion fired and why, and deliberately never records the content it excluded, because that record would be exactly the thing being protected. ### Phase 1 · Not capturing what must not be captured Build: Observe the clipboard, honour the platform marker that flags content as not to be recorded, exclude configured source applications, and detect card numbers, private keys and token shapes. Apply all of it before writing anything. Trap: Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. Done when: Content marked sensitive by the source application produces no stored item, verified by copying from a password manager and inspecting the database directly. An excluded application produces no rows. A card number and a private key pasted from any application are not stored. The skipped record notes the reason and source without the content. Do not build yet: No history, search, collections or sync. ### Phase 2 · A history that is faster than not having one Build: A bounded history with a global hotkey opening a searchable list, keyboard navigable and closing on selection. Measure the time from hotkey to pasted. Preserve the original format including rich text and images. Trap: A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. Done when: Hotkey to pasted is measured at under a second including the first use after a cold start. The list is fully keyboard navigable and closes on selection. Rich text, images and file references paste in their original format. History is bounded and the oldest items are removed rather than growing indefinitely. Do not build yet: No search, collections or encryption yet. ### Phase 3 · Encrypted at rest, searchable in memory Build: Encrypt the database at rest with a key from the platform keychain. Search over decrypted content in memory without writing any index. Lock on idle so the decrypted content is not resident indefinitely. Trap: Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. Done when: The database is unreadable without the key, verified by opening it directly. No plaintext index exists on disk, asserted in the suite. Search across the full bounded history returns as you type. The application locks on idle and decrypted content is cleared from memory when it does. Do not build yet: No collections or sync yet. ### Phase 4 · Things worth keeping Build: Collections of saved items kept indefinitely, distinct from history retention. Pin an item from history into a collection. Apply the same exclusion rules to collections, so a sensitive item cannot be saved deliberately either. Trap: Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. Done when: Collections persist beyond history retention. An item matching an exclusion rule cannot be pinned into a collection, with the reason shown. Collections are searchable alongside history. Reordering and organising collections works without a mouse. Do not build yet: No sync or export yet. ### Phase 5 · Sync, export and forgetting Build: Optional sync of collections only, never history, through storage the user controls. Export collections in a readable format. A clear action that destroys history immediately including any cached copy. Trap: Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. Done when: Only collections sync and history never leaves the machine, verified by inspecting what is transmitted. A clear action destroys history immediately including any in-memory copy, verified after clearing. Export contains collections in a readable format with an explicit plaintext warning. Sync is optional and single-machine use is complete. Do not build yet: Nothing after this. ### Out of scope (and why) - Syncing rolling history across devices, which multiplies the exposure of transient content for very little benefit. - Optical recognition of copied images, which would extract text from screenshots that may contain anything. - Cloud storage of any kind, which turns a local convenience into a hosted repository of everything you copied. - Cross-application automation driven by clipboard content, which is a different tool with a much larger surface. ### README must contain - That exclusions are applied in the capture path, so sensitive content never reaches disk - That the platform sensitivity marker set by password managers is honoured - That the skipped record notes the reason and never the content - That search runs in memory because a plaintext index would make the encryption decorative - That only deliberately saved collections sync, and rolling history never leaves the machine
# Agent instructions · PastePal indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A platform clipboard observer honouring the sensitive-content marker, Source application and content shape rules applied before writing, Encrypted local database with a bounded history, Full text over decrypted content in memory, A local application with a global hotkey. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it.
# Build plan · PastePal A clipboard manager whose interesting engineering is capturing everything fast and searchably, and whose entire design constraint is that a meaningful fraction of what passes through a clipboard must never be written down. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Not capturing what must not be captured Sensitive content never reaches disk in the first place. ### Steps 1. Honour the platform sensitivity marker Password managers set it. Ignoring it is the first failure. Files: `src/capture.ts` 2. Apply shape rules before writing Card numbers, keys and tokens, checked in the capture path. Files: `src/exclusions.ts`, `tests/exclusions.test.ts` 3. Record that something was skipped, not what The record would otherwise be the thing you were protecting. Files: `src/skipped.ts` ### Done when - [ ] Marked content produces no database row - [ ] Excluded applications produce no rows - [ ] Card numbers and keys are never stored - [ ] Skipped records contain no content ### Watch out - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. ## Phase 2 · A history that is faster than not having one Reaching an item from three copies ago takes under a second. ### Steps 1. Pre-warm the window The first use of the day is the one that decides the habit. Files: `src/window.ts` 2. Preserve original formats A rich text item pasted as plain text is a lost item. Files: `src/formats.ts` 3. Bound the history An unbounded clipboard log is a growing liability. Files: `src/retention.ts` ### Done when - [ ] Hotkey to pasted is under a second including cold start - [ ] The list is fully keyboard navigable - [ ] Rich text, images and files paste in original format - [ ] History is bounded and prunes oldest ### Watch out - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. ## Phase 3 · Encrypted at rest, searchable in memory The stored history is unreadable without the key, and still searchable. ### Steps 1. Encrypt with a key from the keychain So the key is not sitting beside the database. Files: `src/crypto.ts` 2. Search in memory, index nothing A plaintext index makes the encryption decorative. Files: `src/search.ts`, `tests/no-index.test.ts` 3. Clear memory on lock Decrypted history resident indefinitely defeats the lock. Files: `src/lock.ts` ### Done when - [ ] The database is unreadable without the key - [ ] No plaintext index exists on disk - [ ] Search is instant across the bounded history - [ ] Locking clears decrypted content from memory ### Watch out - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. ## Phase 4 · Things worth keeping Snippets you use often are saved separately from the rolling history. ### Steps 1. Keep collections outside retention A saved snippet should not roll out of history. Files: `src/collections.ts` 2. Apply exclusions to pinning too Otherwise the sensitive item persists indefinitely rather than briefly. Files: `src/collections.ts` 3. Keep organising keyboard driven The whole tool exists to avoid reaching for a mouse. Files: `src/collections.ts` ### Done when - [ ] Collections persist beyond retention - [ ] Excluded content cannot be pinned, with a reason - [ ] Collections search alongside history - [ ] Organising works without a mouse ### Watch out - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ## Not in this build - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. ## After v1, if you want it - Per-application paste formatting, since pasting into a terminal and a document want different things - A quick transform on paste such as trimming or case changes, which is the next most common need - Smarter shape detection tuned from your own skipped log, which needs no content to improve
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Encrypted history and collections. DB_PATH=~/.local/share/pastepal/history.db # Required · secret. The encryption key lives in the platform keychain, not beside the database. DB_KEY_REF=a keychain item reference # Required. Bounded. An unbounded clipboard log is a growing liability. HISTORY_LIMIT=200 # Required. Applied in the capture path, before anything is written. EXCLUSIONS_PATH=./config/exclusions.txt # Required. Present as a constant. It is how a password manager says do not record this. HONOUR_SENSITIVITY_MARKER=always # Required. Present as a constant. Only deliberately saved collections sync. SYNC_HISTORY=never
# PastePal · product brief ## Problem The core loop is small enough for a capable coding agent to produce a useful local version in one sitting. For PastePal, keep a local clipboard history with collections, search, and sensitive-item rules. The hard boundary is native apple apps, icloud sync, rich content support, and polish, plus os integration polish and maintenance. ## Product outcome A clipboard history fast enough to change how you work, that never wrote down the password you copied five minutes ago. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS 14 or later - Xcode - only the permissions required by the selected feature ## Explicit non-goals for v1 - Syncing rolling history across devices. - Optical recognition of copied images. - Cloud storage of any kind. - Clipboard-driven automation. - native Apple apps, iCloud sync, rich content support, and polish - years of OS-version edge cases - large extension ecosystem - cross-device sync - commercial support and rapid compatibility fixes ## Success criteria - Content marked sensitive is confirmed to produce no row - Card numbers and keys are confirmed never stored - No plaintext index is confirmed on disk - Excluded content is confirmed unpinnable - History is confirmed never to leave the machine
# Build brief · PastePal The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a clipboard history that refuses to keep the things it should not to replace PastePal. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero A clipboard history captures everything you copy, and what people copy includes passwords, recovery codes, card numbers and authentication tokens. A tool that logs all of it to disk has quietly created the most valuable file on the machine, and it did so as a side effect of a convenience feature. So the exclusions come first, before the history exists. Respect the marker that password managers and other applications set to say do not record this. Exclude by source application. Detect the shapes of card numbers, private keys and tokens and never write them. Encrypt what remains at rest. The order matters: exclusion happens before the write, not as a filter when displaying. Something filtered at display is still on disk, still in the backup and still in a crash report, and the only version of this that is defensible is the one where the sensitive value never reached storage at all. ### Stack (fixed, do not substitute) - Capture: A platform clipboard observer honouring the sensitive-content marker · applications signal what should not be recorded and ignoring that is the first failure - Exclusion: Source application and content shape rules applied before writing · filtering at display leaves it on disk - Storage: Encrypted local database with a bounded history · an unbounded clipboard log is a growing liability - Search: Full text over decrypted content in memory · a plaintext index would defeat the encryption - Runtime: A local application with a global hotkey · the value is being faster than switching windows ### Data model (create this before Phase 1) - items(id, kind, content, source_app, copied_at, pinned, collection_id) · kind is text, image, file_ref or rich - collections(id, name, ordinal) · saved snippets, kept indefinitely unlike history - exclusions(id, rule_kind, pattern, created_at) · rule_kind is app, shape or marker - skipped(at, reason, source_app) · that something was excluded, never what it was - settings(key, value) · hotkey, retention, encryption state - The skipped table records that an exclusion fired and why, and deliberately never records the content it excluded, because that record would be exactly the thing being protected. ### Phase 1 · Not capturing what must not be captured Build: Observe the clipboard, honour the platform marker that flags content as not to be recorded, exclude configured source applications, and detect card numbers, private keys and token shapes. Apply all of it before writing anything. Trap: Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. Done when: Content marked sensitive by the source application produces no stored item, verified by copying from a password manager and inspecting the database directly. An excluded application produces no rows. A card number and a private key pasted from any application are not stored. The skipped record notes the reason and source without the content. Do not build yet: No history, search, collections or sync. ### Phase 2 · A history that is faster than not having one Build: A bounded history with a global hotkey opening a searchable list, keyboard navigable and closing on selection. Measure the time from hotkey to pasted. Preserve the original format including rich text and images. Trap: A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. Done when: Hotkey to pasted is measured at under a second including the first use after a cold start. The list is fully keyboard navigable and closes on selection. Rich text, images and file references paste in their original format. History is bounded and the oldest items are removed rather than growing indefinitely. Do not build yet: No search, collections or encryption yet. ### Phase 3 · Encrypted at rest, searchable in memory Build: Encrypt the database at rest with a key from the platform keychain. Search over decrypted content in memory without writing any index. Lock on idle so the decrypted content is not resident indefinitely. Trap: Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. Done when: The database is unreadable without the key, verified by opening it directly. No plaintext index exists on disk, asserted in the suite. Search across the full bounded history returns as you type. The application locks on idle and decrypted content is cleared from memory when it does. Do not build yet: No collections or sync yet. ### Phase 4 · Things worth keeping Build: Collections of saved items kept indefinitely, distinct from history retention. Pin an item from history into a collection. Apply the same exclusion rules to collections, so a sensitive item cannot be saved deliberately either. Trap: Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. Done when: Collections persist beyond history retention. An item matching an exclusion rule cannot be pinned into a collection, with the reason shown. Collections are searchable alongside history. Reordering and organising collections works without a mouse. Do not build yet: No sync or export yet. ### Phase 5 · Sync, export and forgetting Build: Optional sync of collections only, never history, through storage the user controls. Export collections in a readable format. A clear action that destroys history immediately including any cached copy. Trap: Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local. Done when: Only collections sync and history never leaves the machine, verified by inspecting what is transmitted. A clear action destroys history immediately including any in-memory copy, verified after clearing. Export contains collections in a readable format with an explicit plaintext warning. Sync is optional and single-machine use is complete. Do not build yet: Nothing after this. ### Out of scope (and why) - Syncing rolling history across devices, which multiplies the exposure of transient content for very little benefit. - Optical recognition of copied images, which would extract text from screenshots that may contain anything. - Cloud storage of any kind, which turns a local convenience into a hosted repository of everything you copied. - Cross-application automation driven by clipboard content, which is a different tool with a much larger surface. ### README must contain - That exclusions are applied in the capture path, so sensitive content never reaches disk - That the platform sensitivity marker set by password managers is honoured - That the skipped record notes the reason and never the content - That search runs in memory because a plaintext index would make the encryption decorative - That only deliberately saved collections sync, and rolling history never leaves the machine
# Architecture · PastePal ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | A platform clipboard observer honouring the sensitive-content marker | applications signal what should not be recorded and ignoring that is the first failure | | Exclusion | Source application and content shape rules applied before writing | filtering at display leaves it on disk | | Storage | Encrypted local database with a bounded history | an unbounded clipboard log is a growing liability | | Search | Full text over decrypted content in memory | a plaintext index would defeat the encryption | | Runtime | A local application with a global hotkey | the value is being faster than switching windows | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Observing the clipboard and honouring the marker. | Platform specific. Ignoring the marker is the first and worst failure. | | Exclusions | Rules applied before writing. | Not swappable for display filtering, which leaves everything on disk. | | Storage | Encrypted bounded history. | Any store, provided no plaintext index accompanies it. | | Collections | Deliberately saved items under the same rules. | Any implementation, provided exclusions apply to pinning too. | | Sync | Collections only, never history. | Any transport, provided rolling history stays local. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Encrypted history and collections. - `DB_KEY_REF` · required, secret · The encryption key lives in the platform keychain, not beside the database. - `HISTORY_LIMIT` · required · Bounded. An unbounded clipboard log is a growing liability. - `EXCLUSIONS_PATH` · required · Applied in the capture path, before anything is written. - `HONOUR_SENSITIVITY_MARKER` · required · Present as a constant. It is how a password manager says do not record this. - `SYNC_HISTORY` · required · Present as a constant. Only deliberately saved collections sync. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · PastePal product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: A platform clipboard observer honouring the sensitive-content marker, Source application and content shape rules applied before writing, Encrypted local database with a bounded history, Full text over decrypted content in memory, A local application with a global hotkey. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. - Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local.
# Delivery milestones · PastePal Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Not capturing what must not be captured Sensitive content never reaches disk in the first place. ### Steps 1. Honour the platform sensitivity marker Password managers set it. Ignoring it is the first failure. Files: `src/capture.ts` 2. Apply shape rules before writing Card numbers, keys and tokens, checked in the capture path. Files: `src/exclusions.ts`, `tests/exclusions.test.ts` 3. Record that something was skipped, not what The record would otherwise be the thing you were protecting. Files: `src/skipped.ts` ### Done when - [ ] Marked content produces no database row - [ ] Excluded applications produce no rows - [ ] Card numbers and keys are never stored - [ ] Skipped records contain no content ### Watch out - Filtering sensitive items when the history is displayed leaves them in the database, the backups and any crash report, so the protection exists only in the interface. Apply exclusions in the capture path, and record that something was skipped without recording what it was. ## M2 · A history that is faster than not having one Reaching an item from three copies ago takes under a second. ### Steps 1. Pre-warm the window The first use of the day is the one that decides the habit. Files: `src/window.ts` 2. Preserve original formats A rich text item pasted as plain text is a lost item. Files: `src/formats.ts` 3. Bound the history An unbounded clipboard log is a growing liability. Files: `src/retention.ts` ### Done when - [ ] Hotkey to pasted is under a second including cold start - [ ] The list is fully keyboard navigable - [ ] Rich text, images and files paste in original format - [ ] History is bounded and prunes oldest ### Watch out - A clipboard manager that takes two seconds to open is slower than scrolling back in the application you copied from, and people stop using it within a week. Pre-warm the window, and measure from keypress to pasted rather than to displayed. ## M3 · Encrypted at rest, searchable in memory The stored history is unreadable without the key, and still searchable. ### Steps 1. Encrypt with a key from the keychain So the key is not sitting beside the database. Files: `src/crypto.ts` 2. Search in memory, index nothing A plaintext index makes the encryption decorative. Files: `src/search.ts`, `tests/no-index.test.ts` 3. Clear memory on lock Decrypted history resident indefinitely defeats the lock. Files: `src/lock.ts` ### Done when - [ ] The database is unreadable without the key - [ ] No plaintext index exists on disk - [ ] Search is instant across the bounded history - [ ] Locking clears decrypted content from memory ### Watch out - Building a search index for speed writes the content in plaintext beside the encrypted database, which makes the encryption decorative. Search in memory over the decrypted set, and accept the performance cost, which at a bounded history size is negligible. ## M4 · Things worth keeping Snippets you use often are saved separately from the rolling history. ### Steps 1. Keep collections outside retention A saved snippet should not roll out of history. Files: `src/collections.ts` 2. Apply exclusions to pinning too Otherwise the sensitive item persists indefinitely rather than briefly. Files: `src/collections.ts` 3. Keep organising keyboard driven The whole tool exists to avoid reaching for a mouse. Files: `src/collections.ts` ### Done when - [ ] Collections persist beyond retention - [ ] Excluded content cannot be pinned, with a reason - [ ] Collections search alongside history - [ ] Organising works without a mouse ### Watch out - Allowing a user to pin an item that the exclusion rules would have blocked reintroduces exactly the risk, and the item then persists indefinitely rather than rolling out of history. Apply the same rules to collections and explain the refusal rather than silently allowing it. ## M5 · Sync, export and forgetting (production only) History can move between machines or be destroyed completely. ### Steps 1. Sync collections only History is transient by definition and multiplying it is pure exposure. Files: `src/sync.ts` 2. Make clearing immediate and total Including memory, not only the database. Files: `src/clear.ts` 3. Warn plainly on export An exported collection file is plaintext by nature. Files: `src/export.ts` ### Done when - [ ] History never leaves the machine - [ ] Clearing destroys history including memory - [ ] Export warns that the file is plaintext - [ ] Single-machine use is complete without sync ### Watch out - Syncing the rolling history rather than only saved collections multiplies the exposure across every device and every backup of the sync store, for a set of items that are transient by definition. Sync what was deliberately saved, and leave the history local.
# Operations · PastePal ## Backup Collections are worth backing up and history is not, which is convenient because history is the sensitive half. Back up collections and let history be genuinely transient. ## Restore Restore collections and confirm no history came back, since a restored history reintroduces content the retention had already discarded. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a personal tool the check is that exclusions are firing at all. A skipped count of zero over a week almost certainly means the marker is not being read rather than that nothing sensitive was copied. ## Incident checklist A password appearing in the history is the failure, and it means either the marker was not honoured or a shape rule was missing. The skipped log tells you which without telling you what. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Content marked sensitive is confirmed to produce no row - [ ] Card numbers and keys are confirmed never stored - [ ] No plaintext index is confirmed on disk - [ ] Excluded content is confirmed unpinnable - [ ] History is confirmed never to leave the machine ## Launch constraint Do not market omitted PastePal capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Encrypted history and collections. DB_PATH=~/.local/share/pastepal/history.db # Required · secret. The encryption key lives in the platform keychain, not beside the database. DB_KEY_REF=a keychain item reference # Required. Bounded. An unbounded clipboard log is a growing liability. HISTORY_LIMIT=200 # Required. Applied in the capture path, before anything is written. EXCLUSIONS_PATH=./config/exclusions.txt # Required. Present as a constant. It is how a password manager says do not record this. HONOUR_SENSITIVITY_MARKER=always # Required. Present as a constant. Only deliberately saved collections sync. SYNC_HISTORY=never
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for PastePal because small utilities are technically reproducible, but customers pay for polish and immediate fixes when an OS update breaks a daily habit. The recurring cost buys permissions, accessibility APIs, sandboxing, signing, notarization, OS regressions, updates, backups, and support, not just the visible interface.
xnative Apple apps, iCloud sync, rich content support, and polish
xyears of OS-version edge cases
xlarge extension ecosystem
xcross-device sync
xcommercial support and rapid compatibility fixes
Don't feel like building it? These folks already made it free.
CCopyQTabs, search, rules, ignored apps, and encrypted storage; the interface is very Linux even on a Mac.open source↗no votes, no pay-to-list · just what's real
PastePal pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| app store universal | custom | — | Mac, iPhone, and iPad; iCloud sync; Family Sharing; lifetime updates. |
| standalone mac | custom | — | 1 Mac only; no iPhone/iPad app and no iCloud sync; lifetime updates. |
free tierno free tier; the app can be downloaded to try, but pasting requires the $14.99 unlock and no numeric trial cap is published
billingone-time purchase; no monthly or annual plan
hidden costsThe same-priced standalone licence covers only 1 Mac and excludes iCloud sync and the iPhone/iPad apps; the App Store version is the broader licence.
verified 2026-08-14 · source ↗
Vibecode PastePal
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal PastePal replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does PastePal cost?
PastePal's pricing is usage-based or varies by plan · Sold primarily as a one-time licence; source page should be rechecked before merge..
What do I lose by replacing PastePal?
Honestly: native Apple apps, iCloud sync, rich content support, and polish; years of OS-version edge cases; large extension ecosystem; cross-device sync; commercial support and rapid compatibility fixes. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to PastePal?
Yes: CopyQ (Tabs, search, rules, ignored apps, and encrypted storage; the interface is very Linux even on a Mac.) The prompt is for when you want it exactly your way.