Olympus Docs
InternalsDaedalus

Daedalus, secrets sanitizer

How Daedalus prevents secrets leaking through the terminal display

Many Daedalus commands print secrets to stdout: gh auth status, doctl auth init, cat /opt/olympus/.env.prod. Without sanitization, these would:

  • Show on the operator's screen (visible to anyone with shoulder access).
  • Get captured in MCP screenshot tool output (which Claude / other agents see).
  • Get logged to Daedalus's optional session log file.

The sanitizer redacts known-secret patterns before display.

Where it runs

In daedalus/src-tauri/src/commands/sanitize.rs, called from the PTY reader task on every output chunk:

fn sanitize_secrets(input: &str) -> String {
    let mut result = input.to_string();
    for pat in SECRET_PATTERNS.iter() {
        result = pat.regex.replace_all(&result, pat.replacement).into_owned();
    }
    result
}

Patterns

static SECRET_PATTERNS: Lazy<Vec<Pattern>> = Lazy::new(|| vec![
    // Bearer / Authorization tokens
    Pattern::new(r"(?i)bearer\s+[A-Za-z0-9._-]{20,}", "Bearer [REDACTED]"),
    Pattern::new(r"(?i)authorization:\s*[^\r\n]+", "Authorization: [REDACTED]"),

    // GitHub tokens
    Pattern::new(r"ghp_[A-Za-z0-9]{36,}", "ghp_[REDACTED]"),
    Pattern::new(r"gho_[A-Za-z0-9]{36,}", "gho_[REDACTED]"),
    Pattern::new(r"ghu_[A-Za-z0-9]{36,}", "ghu_[REDACTED]"),
    Pattern::new(r"ghr_[A-Za-z0-9]{36,}", "ghr_[REDACTED]"),
    Pattern::new(r"ghs_[A-Za-z0-9]{36,}", "ghs_[REDACTED]"),

    // DigitalOcean
    Pattern::new(r"dop_v1_[a-f0-9]{60,}", "dop_v1_[REDACTED]"),

    // Resend
    Pattern::new(r"re_[A-Za-z0-9]{20,}", "re_[REDACTED]"),

    // Postmark server tokens
    Pattern::new(r"[A-Za-z0-9-]{36}", |m| {
        // UUID-shape, could be a Postmark token. Conservative.
        if m.contains("-") && m.len() == 36 { "[REDACTED-UUID]".to_string() } else { m.to_string() }
    }),

    // PostgreSQL DSN passwords
    Pattern::new(r"postgres(?:ql)?://[^:]+:([^@]+)@", "postgres://[REDACTED-USER]:[REDACTED]@"),

    // JWT-like strings
    Pattern::new(r"eyJ[A-Za-z0-9._-]+\.eyJ[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+", "[REDACTED-JWT]"),

    // Generic high-entropy strings (last resort)
    // ... (omitted; high false-positive rate)
]);

The list grows as we discover new secret formats.

False positives

Some non-secret strings match. Examples:

  • A UUID that's actually a database row ID gets [REDACTED-UUID].
  • A base64-encoded image data URL might trigger a JWT-like pattern.

Trade-off: we err on the side of over-redaction. An operator who needs to see a redacted value can re-run the command outside Daedalus to get the raw output.

False negatives

Some secret formats we don't catch:

  • Custom API keys without a recognizable prefix.
  • Secrets embedded in JSON values that don't look like tokens at the regex level.

For these, the operator's responsibility increases. The wizard's design tries not to dump raw config files into the terminal, most secret reads happen via Daedalus's UI fields, not via terminal commands.

What's NOT sanitized

  • The Daedalus UI itself. The Secrets wizard page shows secrets as the operator enters them, by design.
  • Daedalus's persistent state file daedalus.json. Stored unredacted.

The sanitizer is only for the terminal pipeline + log + screenshot path.

Disabling

For debugging the sanitizer itself: DAEDALUS_SANITIZER_DISABLE=true env var. Strongly recommend not using this except in development.

Testing the sanitizer

cd daedalus/src-tauri
cargo test sanitize

Tests verify:

  • Known patterns are redacted.
  • Non-patterns are preserved.
  • Adjacent text isn't munged.

On this page