Back to Articles

Go-Flashpaper: Building a Memory-Only Secret Sharing Service with Honeypot Forensics

[ View on GitHub ]

Go-Flashpaper: Building a Memory-Only Secret Sharing Service with Honeypot Forensics

Hook

Most secret-sharing services write your data to disk before encrypting it. Go-flashpaper takes the opposite approach: your secrets never touch persistent storage, existing only in RAM until accessed once or purged after 24 hours.

Context

Sharing sensitive information—API keys, credentials, deployment tokens—has always been a security minefield. Email leaves persistent copies on multiple servers. Slack archives everything. Even encrypted messaging apps maintain local databases. The traditional solution, password managers with sharing features, requires both parties to use the same ecosystem and maintain accounts.

One-time secret sharing services emerged to solve this: generate a URL, share it once, and the secret self-destructs. But most implementations use databases or file systems, creating audit trails and recovery vectors that security teams hate. Go-flashpaper was built on a single radical premise: what if secrets never touched disk at all? By keeping everything in memory and intentionally discarding data on restart, it sacrifices availability for a security model that's trivial to audit and impossible to exfiltrate through traditional persistence-layer attacks.

Technical Insight

POST secret

Generate URL

32-byte token

Return URL

GET /token

Lookup & Delete

Secret found once

Already consumed

Purge >24h secrets

Auto TLS

Client Browser

HTTP Server :8443

BasicAuth Middleware

In-Memory Map

sync.RWMutex

crypto/rand

Token Generator

Cleanup Goroutine

10min interval

Canarytoken Alert

Let's Encrypt

TLS Certs

System architecture — auto-generated

Go-flashpaper's architecture is deceptively simple: a Go HTTP server with an in-memory map storing secrets keyed by cryptographically random identifiers. When you POST data to the service, it generates a 32-byte random token using crypto/rand, stores your secret in a concurrent-safe map, and returns a URL. Access that URL once, and the secret is retrieved and immediately deleted. Access it again, and you get a 404—or optionally, a Canarytoken alert fires to your security team.

The core data structure looks like this:

type Secret struct {
    Data      []byte
    Filename  string
    CreatedAt time.Time
    IsFile    bool
}

var secrets = struct {
    sync.RWMutex
    m map[string]*Secret
}{m: make(map[string]*Secret)}

Every 10 minutes, a goroutine sweeps through the map, purging secrets older than 24 hours. This time-based expiry acts as a backstop against abandoned secrets consuming memory indefinitely. The implementation deliberately uses sync.RWMutex for concurrent access rather than more sophisticated data structures—this keeps the codebase auditable by anyone familiar with Go's standard library.

What sets go-flashpaper apart is its honeypot functionality via Canarytoken integration. When a user accesses an already-consumed secret, the service can trigger a Canarytoken URL with the requester's IP, User-Agent, and timestamp. This turns expired links into forensic tools: if an attacker intercepts a secret link and tries using it after the legitimate recipient already accessed it, your security team gets an alert with attribution data.

The TLS implementation leverages Go's autocert package for automatic Let's Encrypt certificate provisioning:

certManager := autocert.Manager{
    Prompt:     autocert.AcceptTOS,
    HostPolicy: autocert.HostWhitelist("yourdomain.com"),
    Cache:      autocert.DirCache("/var/cache/flashpaper"),
}

server := &http.Server{
    Addr:      ":8443",
    TLSConfig: certManager.TLSConfig(),
}

This eliminates the operational burden of certificate management while ensuring all secret transmissions occur over encrypted channels. The service runs on port 8443 by default (non-privileged), making it safe to run as a non-root user.

The zero-persistence model requires operational discipline: you must disable swap on the host system, or secrets could be paged to disk during memory pressure. Deployments typically use systemd units with MemorySwapMax=0 or kernel parameters like vm.swappiness=0. This trades convenience for security—your secrets genuinely cannot leak through disk forensics because they never touch persistent storage.

Optional BasicAuth support reads credentials from a simple file, allowing you to restrict who can create secrets without implementing full user management. For internal team deployments, this hits the sweet spot between completely open (dangerous) and OAuth integration (overkill).

Gotcha

The biggest limitation is what makes it secure: the memory-only storage model means zero fault tolerance. Service restart? All secrets gone. OOM-killer strikes? All secrets gone. There's no way to distinguish between "this link was already accessed" and "the service restarted, sorry." Users just get a 404 either way. For internal team use, this is annoying. For production services with external users, it's a showstopper.

There's also no rate limiting, abuse prevention, or resource quotas beyond the hardcoded 10MB file size limit. An attacker (or careless user) can trivially exhaust your server's memory by uploading hundreds of 10MB files. There's no IP-based throttling, no CAPTCHA, no authentication requirement for secret creation (unless you enable BasicAuth globally). The service trusts that you're deploying it in an environment where all users are trusted, or that you've placed it behind another service handling abuse prevention. Exposing go-flashpaper directly to the public internet without additional protections is asking for trouble. The single-server architecture also means no horizontal scaling—you can't run multiple instances sharing state because there is no state layer to share.

Verdict

Use if: You need a dead-simple, auditable solution for one-time secret sharing within a trusted network or for internal team use. The memory-only storage and minimal dependency surface make it excellent for security-conscious organizations that value auditability over features. The Canarytoken integration is particularly clever for security teams who want forensic data on potential secret interception. Perfect for self-hosted internal tooling where you control the deployment environment and can accept occasional downtime. Skip if: You need high availability, public internet exposure, or any kind of scale beyond a small team. The lack of rate limiting makes it dangerous to expose publicly. The no-persistence model means you can't provide uptime guarantees. If you need Redis-backed clustering, user management, or API rate limits, look at Yopass instead. If you're sharing secrets with external users who expect reliability, the "all secrets purged on restart" behavior will create support headaches you don't want.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/data-knowledge/rawdigits-go-flashpaper.svg)](https://starlog.is/api/badge-click/data-knowledge/rawdigits-go-flashpaper)