Skip to content

Kbayero/qubtrace

Repository files navigation

QubTrace

Give your AI assistant a persistent memory of everything you do on your computer.

LLMs are powerful but stateless — every conversation starts from zero. QubTrace runs silently in the background, recording your developer activity, and exposes it to any AI agent via MCP. Your AI now knows what you were working on, what commands you ran, what files you changed, what sites you visited — without you having to explain anything.

Ask Claude: "What was I working on yesterday?"
            "Why did I change that file last week?"
            "Show me every command I ran before this bug appeared"

Everything stays on your machine. No cloud, no accounts, no telemetry.


How it works

Shell / Git / VS Code / Browser / SSH
              ↓
       QubTrace daemon          ← secrets stripped before writing
              ↓
       SQLite  (~/.qubtrace/)   ← local, private
              ↓
        MCP server (stdio)
              ↓
     Claude / Cursor / any LLM

Platform support

OS Status
macOS (Apple Silicon & Intel) ✅ Supported
Linux (x86-64, ARM64) ✅ Supported
Windows 🔜 Coming soon

Two moving parts:

  1. Daemon — lightweight background service that receives events from collectors, runs them through processors (sanitizer, embeddings), and writes to SQLite.
  2. MCP server — started on demand by your LLM client via qubtrace mcp. Reads from SQLite and exposes structured tools to the model.

Quick start

1. Build and install

git clone https://github.com/qubcore/qubtrace
cd qubtrace
go build -o /tmp/qubtrace ./cmd/qubtrace && /tmp/qubtrace install

qubtrace install does three things in one step:

  • Copies the binary to ~/.local/bin/qubtrace
  • Installs and starts the background daemon (launchd on macOS, systemd on Linux)
  • Enables the shell collector for your current shell

Make sure ~/.local/bin is on your PATH:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc

Open a new terminal — recording starts immediately.

2. Connect your AI

Claude Code — add to ~/.claude.json:

{
  "mcpServers": {
    "qubtrace": {
      "command": "qubtrace",
      "args": ["mcp"]
    }
  }
}

Cursor — Settings → MCP → add server, same command/args.

Any MCP-compatible client — use {"type":"stdio","command":"qubtrace","args":["mcp"]}.

Restart your client after editing the config. The MCP server is started on demand (stdio) — it does not need to be running beforehand.


Collectors

Each collector is independent. Enable only what makes sense for your workflow.

Collector What it captures Enable
shell Every command, exit code, duration, working directory qubtrace enable shell
git Every git operation — branch, hash, message, remote qubtrace enable git
vscode File saves, creates, deletes, renames qubtrace enable vscode
ssh Command and session output of every SSH connection qubtrace enable ssh
chrome Page navigations and clicks in Chromium-based browsers qubtrace enable chrome
qubtrace enable <name>      # install dependencies + activate
qubtrace disable <name>     # deactivate (keeps dependencies)
qubtrace uninstall <name>   # deactivate + remove dependencies

enable is idempotent — safe to run multiple times.

Browser setup (one-time manual step)

Chrome's security model requires loading the extension manually once:

qubtrace enable chrome

The command writes the extension to disk and prints the exact steps:

  1. Open chrome://extensions
  2. Enable Developer Mode (toggle, top-right)
  3. Click Load unpacked → select the folder printed by the command
  4. Done — navigations and clicks are recorded from now on

Works with Chrome, Brave, and Edge.

Domain allowlist (optional)

By default the Chrome collector records all sites. To restrict it to specific domains:

qubtrace collector set chrome allowed_domains "github.com,notion.so,linear.app"

Subdomains are included automatically (docs.github.com matches github.com). Set to empty string to record all sites again:

qubtrace collector set chrome allowed_domains ""

The domain filter applies both in the extension and server-side — events from blocked domains are dropped before they reach the pipeline.


Privacy — secrets never reach disk

The Sanitizer processor runs on every event before it is written to SQLite. It strips:

Rule Example
URL credentials https://user:token@hosthttps://user:[REDACTED]@host
Environment secrets API_KEY=abc123API_KEY=[REDACTED]
Password flags --password=secret--password=[REDACTED]
JWT tokens eyJhbGci…[REDACTED_JWT]
AWS keys AKIA…[REDACTED_AWS_KEY]
Auth headers Authorization: Bearer …Authorization: Bearer [REDACTED]
GitHub tokens ghp_…[REDACTED_GH_TOKEN]

The sanitizer is enabled by default. Toggle individual rules via CLI or YAML — changes take effect immediately without a daemon restart:

# toggle a built-in rule
qubtrace processors set sanitizer rules.jwt_tokens false

# add a custom regex pattern
qubtrace processors custom add "MY_CORP_SECRET=[^\s]+"
qubtrace processors custom list
qubtrace processors custom remove 1

~/.qubtrace/processors/sanitizer.yml (direct YAML editing also works):

rules:
  url_credentials: true
  env_secrets: true
  password_flags: true
  jwt_tokens: true
  aws_keys: true
  auth_headers: true
  github_tokens: true
custom:
  - pattern: "MY_INTERNAL_SECRET=[^\s]+"
    description: "Internal secret pattern"
    enabled: true

CLI reference

# Install & service
qubtrace install                        # full one-step install
qubtrace service install                # register daemon with OS init system
qubtrace service start
qubtrace service stop
qubtrace service status
qubtrace service uninstall

# Pipeline overview
qubtrace status                         # all collectors, processors, and sinks at a glance

# Collectors
qubtrace collectors                     # list all with enabled/deps status
qubtrace enable <name>                  # install deps + activate
qubtrace disable <name>                 # deactivate (keeps deps)
qubtrace uninstall <name>               # deactivate + remove deps

# Collector config
qubtrace collector config <name>        # show a collector's current config as YAML
qubtrace collector set chrome allowed_domains "github.com,stackoverflow.com"
                                        # filter Chrome to specific domains (empty = all)

# Processors
qubtrace processors                     # list all with enabled status
qubtrace processors enable <name>       # enable a processor
qubtrace processors disable <name>      # disable a processor
qubtrace processors config <name>       # show current config as YAML
qubtrace processors set <name> <key> <value>          # set a top-level config value
qubtrace processors set <name> <key>.<subkey> <value> # set a nested config value

# examples:
qubtrace processors set sanitizer rules.jwt_tokens false
qubtrace processors set sanitizer rules.aws_keys true
qubtrace processors set embeddings model nomic-embed-text

# Custom sanitizer patterns (take effect immediately, no restart needed)
qubtrace processors custom list
qubtrace processors custom add "MY_CORP_SECRET=[^\s]+"
qubtrace processors custom remove 1

# Sinks
qubtrace sinks                          # list all sinks

# Events
qubtrace events                         # last 20 events (table)
qubtrace events --limit 100
qubtrace events --source terminal.zsh
qubtrace events --session <id>

qubtrace export                         # JSON export, ready for LLMs
qubtrace export --source git --limit 0  # all git events

# MCP
qubtrace mcp                            # start MCP server (stdio)

# Semantic search (optional)
qubtrace embeddings setup               # install Ollama + pull model + enable
qubtrace embeddings status              # show stats and Ollama health

# Maintenance
qubtrace cleanup                        # apply retention policy now (also runs daily in daemon)

MCP tools

Once connected, your LLM can call:

Tool What it does
events Query activity by source, time range, session, or keyword
session Full output of a recorded shell or SSH session
summary Activity breakdown by tool for any time window
semantic_search Find events by meaning via vector similarity (requires embeddings)

Example questions you can ask your AI:

  • "What was I working on before I got interrupted?"
  • "List all git commits I made this week"
  • "What command did I run to fix that Docker error last month?"
  • "Which sites was I visiting while debugging that networking issue?"
  • "Find anything related to the payment integration" (semantic search)

Semantic search (optional)

Enable vector embeddings so your AI can search by meaning, not just keywords:

qubtrace embeddings setup
# installs Ollama, pulls nomic-embed-text, enables the embeddings processor

qubtrace service stop && qubtrace service start
# restart the daemon to begin generating embeddings for new events

Check status at any time:

qubtrace embeddings status
# embeddings enabled: true
# ollama url:         http://localhost:11434
# model:              nomic-embed-text
# ollama running:     true
# events total:       1482
# events embedded:    1480 (99%)

Data retention

By default the daemon automatically deletes old data on startup and once every 24 hours:

Policy Default
Events older than N days 90 days
DB size cap (oldest first) 2 GB
Session recordings older than N days 30 days

Run it manually at any time:

qubtrace cleanup
# retention policy: max_age=90 days  max_db=2.0 GB  sessions=30 days
# events deleted:   1204
# sessions deleted: 38
# db size:          847.2 MB → 312.0 MB

Customize in ~/.qubtrace/retention.yml:

max_age_days: 90          # delete events older than this
max_db_gb: 2.0            # trim oldest events if DB exceeds this
sessions_max_age_days: 30 # delete session recordings older than this

Set any value to 0 to disable that check (e.g. max_db_gb: 0 to skip the size cap).


Data layout

~/.qubtrace/
├── qubtrace.db              # SQLite: events + embeddings
├── cmd.log                  # ring buffer: shell/git hooks write here, daemon reads+truncates
├── retention.yml            # cleanup policy (optional, defaults above apply if absent)
├── sessions/                # full PTY recordings (secrets stripped)
├── collectors/
│   ├── shell.yml            # .yml = enabled, .disabled = disabled
│   ├── git.yml
│   ├── chrome.yml
│   └── ...
└── processors/
    ├── sanitizer.yml        # redaction rules + custom patterns
    └── embeddings.yml       # Ollama URL + model

Config files are plain YAML. The daemon watches both directories with fsnotify and applies changes without a restart — collector enable/disable and sanitizer custom patterns take effect immediately.


Extending QubTrace

Sending events from external tools

When the Chrome collector is enabled it opens an HTTP server at 127.0.0.1:7335 that also accepts events from any local process:

# requires: qubtrace enable chrome
curl -s -X POST http://127.0.0.1:7335/v1/events \
  -H 'Content-Type: application/json' \
  -d '{
    "source":    "mytool.action",
    "command":   "deployed v1.2.3 to staging",
    "cwd":       "/my/project",
    "exit_code": 0
  }'

Adding a collector

  1. Create a package under collectors/ that implements collectors/types.Collector:
type Collector interface {
    Manifest() Manifest                              // name, description, sources
    Run(ctx context.Context, emit func(Event)) error // blocking; call emit() for each event
    InstallDependencies() error                      // write files, install hooks, etc.
    UninstallDependencies() error
    Enable() error                                   // Base.Enable() renames .disabled → .yml
    Disable() error                                  // Base.Disable() renames .yml → .disabled
    UpdateConfig(fields map[string]any) error
    IsEnabled() bool
    IsDepsInstalled() bool
}
  1. Call types.InitDisabled("myname", defaultConfig) in New() to create the initial config file.

  2. Register it in collectors/registry.go:

var registry = []types.Collector{
    shell.New(),
    git.New(),
    // ...
    mypkg.New(),   // add here
}

The daemon picks it up automatically — no other changes needed.

Adding a processor

  1. Create a package under processor/ that implements processor/types.Processor:
type Processor interface {
    Manifest() Manifest
    Process(ctx context.Context, e event.Event) (event.Event, error) // transform or enrich
    Enable() error
    Disable() error
    IsEnabled() bool
    InstallDependencies() error
    UninstallDependencies() error
}
  1. Optionally implement types.ConfigReloader so the daemon hot-reloads your config when the YAML changes:
func (p *MyProcessor) ReloadConfig() {
    types.ReadConfig("myprocessor", p)
    p.rebuildInternalState()
}
  1. Register it in processor/registry.go.

Adding a sink

  1. Implement sink/types.Sink:
type Sink interface {
    Manifest() Manifest
    Run(ctx context.Context, in <-chan event.Event) error  // drain the channel
}
  1. Register it in sink/registry.go. The daemon starts all registered sinks automatically — no config file required.

Uninstall

qubtrace service stop && qubtrace service uninstall
qubtrace uninstall shell git vscode ssh chrome
rm -rf ~/.qubtrace ~/QubTrace
rm ~/.local/bin/qubtrace

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages