Skip to content

Pinned and named clips — persistent credential-friendly history entries #20

@djdarcy

Description

@djdarcy

Pinned and named clips — persistent credential-friendly history entries

Problem

teeclip's clipboard history uses FIFO eviction: when max_entries is reached (default 50), the oldest entry is silently deleted. This works well for general clipboard content but creates a problem for users who rely on teeclip as a lightweight credential store.

Consider this workflow:

# User copies API key from password manager → saved to encrypted history as entry #1
# User does 50 more clipboard operations...
# API key is silently evicted from history
teeclip --get 50   # error: no clip at index 50 — the key is gone

Additionally, retrieving credentials by numeric index (--get 3) is fragile — the index shifts every time a new clip is added. Users need a stable way to reference important entries.

Proposed solution

Two complementary features that make teeclip viable as a simple encrypted credential helper:

Pinned clips

Pin entries to protect them from FIFO eviction:

teeclip --pin 3              # pin entry #3 (exempt from eviction)
teeclip --unpin 3            # unpin entry #3 (returns to normal FIFO)
teeclip --list               # pinned entries show [P] marker

Pinned entries remain in history indefinitely regardless of max_entries. FIFO eviction only applies to unpinned entries.

Named clips

Save and retrieve clips by name instead of numeric index:

# Save with a name
teeclip --save-as "aws-key"                          # save clipboard as named clip
echo "sk-abc123" | teeclip --name "openai-key"       # pipe content into named clip

# Retrieve by name
teeclip --get aws-key | deploy-tool --key-stdin
teeclip --get openai-key                              # also copies back to clipboard

# List shows names
teeclip --list
#   1  2026-02-18 09:30 [P]  aws-key: AKIA...
#   2  2026-02-18 09:28 [P]  openai-key: sk-a...

Named clips are implicitly pinned (exempt from eviction). The name provides stable, human-readable retrieval.

Schema changes

-- Add to clips table:
ALTER TABLE clips ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;
ALTER TABLE clips ADD COLUMN name TEXT DEFAULT NULL;

-- Unique constraint on names:
CREATE UNIQUE INDEX IF NOT EXISTS idx_clips_name ON clips(name) WHERE name IS NOT NULL;

Implementation approach

Phase 1 — Pinned clips:

  • Add pinned column to schema (v3 migration)
  • Modify FIFO eviction query: DELETE FROM clips WHERE pinned = 0 AND id NOT IN (SELECT id FROM clips WHERE pinned = 0 ORDER BY id DESC LIMIT ?)
  • Add --pin N and --unpin N CLI flags
  • Show [P] marker in --list output (alongside existing [E] for encrypted)

Phase 2 — Named clips:

  • Add name column to schema (same v3 migration)
  • Add --save-as NAME and --name NAME CLI flags
  • Extend --get to accept a string name in addition to integer index
  • Show name in --list preview when present

Design considerations

  • Named clips are implicitly pinned: If you took the time to name something, you don't want it evicted. This avoids the confusing state of a named clip that disappears.
  • --get overloading: --get 3 (integer → index) vs --get aws-key (string → name). Disambiguation: try integer parse first. Names that are pure digits would need quoting or a prefix convention — but this edge case is rare enough to handle later.
  • max_entries interaction: The limit applies to unpinned entries only. If a user pins 100 entries, the database holds 100 + max_entries. This is acceptable for a developer workstation tool.
  • Selective clear interaction: --clear should skip pinned entries by default. --clear --force or explicit --unpin + --clear to remove pinned entries.
  • Encryption: Pinned/named clips follow the same encryption rules as regular clips. Names are stored in plaintext (they're user-chosen labels, not secret content).

Acceptance criteria

  • pinned column exists in clips table (schema v3)
  • name column exists in clips table with unique constraint (schema v3)
  • --pin N pins an entry; --unpin N unpins it
  • Pinned entries are exempt from FIFO eviction
  • --list shows [P] marker for pinned entries
  • --save-as NAME saves clipboard contents with a name
  • --name NAME during pipe saves piped content with a name
  • --get NAME retrieves by name; --get N retrieves by index
  • Named clips are implicitly pinned
  • --clear skips pinned entries by default
  • Tests for pin/unpin/named CRUD operations
  • Tests for FIFO eviction respecting pinned entries

Related issues

Analysis

See notes/ideas/2026-02-18__09-18-22__both_secure-credential-piping-via-clipboard.md for the original observation and discussion about teeclip as a credential helper.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature requestideasExploratory ideas and future possibilitiessecuritySecurity, encryption, and data protection

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions