- TFEP envelope schema + JSON serialization (
pkg/tfep/envelope.go) - Ed25519 signing over canonical SHA-256 of the envelope (
pkg/tfep/sign.go) - Signature verification (message + public key → bool)
- X25519 ECDH key exchange + NaCl secretbox encrypt/decrypt (
pkg/tfep/crypto.go) - Hashcash proof-of-work generator and validator (
pkg/tfep/pow.go) - Unit tests: sign/verify, encrypt/decrypt, PoW round-trips — 8/8 passing
-
LookupCapability(domain)— parse_tfepDNS TXT record -
ResolveDID(did)— fetchdid:webdocument, extract Ed25519 + X25519 keys - TTL-aware cache for DNS + DID results
- Unit tests with stubbed DNS/HTTP — 3/3 passing
-
IssuePermit(issuer, grantee, expiry)→ signed JWT -
ValidatePermit(token, issuerPublicKey)→ bool -
IssueRelationshipTokenfor transactional senders - Auto-issue permit wired in
queue releaseCLI command
-
go-smtpbackend, configurable port (default 2525) - Per-message pipeline: identity lookup → tier → type check → permit check → queue
- 554 reject for
marketingwith no opt-in permit -
X-TFEP-Spoof-Warning+ tier downgrade for display-name mismatch - Transactional relationship token check → Unknown tier if missing
- Compute PoW if required
- Sign envelope, encrypt body (if recipient has X25519 key)
- Construct MIME with
application/tfep+json+application/tfep-proofparts - Relay to recipient MX; embed
X-TFEP-Proofheader
-
AssignTier(senderInfo) Tier(internal/trust/tier.go) -
IssueReputationToken— legacy SMTP→TFEP bootstrap JWT (internal/trust/reputation.go) -
DecayScore— exponential decay with configurable half-life -
IsSpoofedDisplayName— brand-name phishing detection -
MessageTypeRule— enforces permit/relationship-token requirements
- Schema:
messages,permits,reputation_tokenstables - Migration runner (no ORM)
- CRUD helpers: Insert/Get/List/Update messages; Upsert/Get/List/Delete permits
- Thread query via
ListMessages(threadID=...)
-
Configstruct +LoadConfigfromconfig.yaml+ env overrides -
Server.Start()wires SMTP server, identity resolver, store - Graceful shutdown on SIGINT/SIGTERM with drain
-
tfep-gateway serve -
tfep-gateway keygen --domain— generates Ed25519/X25519 keys, DID doc, DNS TXT record -
tfep-gateway send --to --type --subject --body -
tfep-gateway queue list [--tier] [--type] [--thread] -
tfep-gateway queue release <id>— promotes + auto-issues permit -
tfep-gateway permits list|revoke
-
Dockerfile— multi-stage, distroless final image -
docker-compose.yml— single service with volumes -
tfep-gateway.service— systemd unit with hardening flags -
config.yaml— annotated defaults
- A1 TLS/STARTTLS —
internal/smtp/server.go(NewServer+NewSubmissionServer), separate MTA port (:25) and submission port (:587),tls.Configfrom cert+key files - A2 Rate limiting —
internal/ratelimit/ratelimit.go, token bucket per IP (connections/min) and per sender DID (messages/hour), 1-hour temporary block at 3× burst - A3 Replay attack prevention —
internal/smtp/server.goprocess(), rejects messages withtimestampolder thantrust.max_message_age(default 5m) or too far in the future (trust.clock_skew) - A4 Encrypted key storage —
internal/keystore/keystore.go, AES-256-GCM + PBKDF2-SHA256, passphrase from CLI flag orTFEP_KEY_PASSPHRASEenv var - A5 DNSSEC validation —
internal/identity/capability.go, checks DNSADbit, warns if not set, hard-rejects ifidentity.require_dnssec: true
- B1 REST API —
internal/api/server.go, 18 endpoints (messages, permits, contacts, stats, well-known permit/unsubscribe), Bearer token auth - B2 Prometheus metrics —
internal/metrics/metrics.go, counters for received/rejected messages, queue depth gauges, relay results, cache hits - B3 Structured logging —
internal/logger/logger.go,log/slogwith JSON or text handler, log level from config - B4 Minimal web UI —
internal/api/static/index.html, vanilla JS SPA, inbox + held queue + permits + contacts,go:embed
- C1 Key rotation —
tfep-gateway rotate-key, new keys written to*.new.pem,notBeforetimestamp in DID document; identity resolver skipsrevoked: trueand future-dated keys - C2 DKIM signing —
internal/smtp/client.godkimSign(),github.com/emersion/go-msgauth/dkim, config fieldsidentity.dkim_selector+identity.dkim_key_file - C3 Delivery receipts/NDR —
pkg/tfep/envelope.goTypeReceipt/TypeBounce+BouncedMessageID/BounceReason/BounceCodefields;SendBounce()ininternal/smtp/client.go - C4 Protocol version negotiation —
caps=field in_tfepDNS record, parsed intoCapability.Caps, set in outbound envelopeRecipientCaps;versionfield in envelope - C5 Multi-recipient support —
Envelope.Recipients []RecipientEntrywith per-recipientEphemeralKey+WrappedContentKeyfields
- D1 Message retention/cleanup —
store.DeleteMessagesBefore(), background hourly goroutine ingateway.Server,tfep-gateway store vacuumCLI command, configurable per-tier TTLs - D2 Multi-domain config structure — Config expanded to support
identities[]list (foundation);keygenandrotate-keyoutput documents ready for multiple domains
- E1 Federated reputation —
internal/trust/federated.go, DNS TXT query_tfep-rep.<domain>.<resolver>, returns score + report count; disabled by default (trust.federated_reputation.enabled: false) - E2 Contact book —
store.Contact+InsertContact/ListContacts/DeleteContact,contactsSQLite table, REST API endpointsGET/POST/DELETE /api/v1/contacts
- PROTOCOL.md — 15-section formal spec covering identity model, DNS records, envelope schema, message types, trust tiers, permits, encryption, PoW, anti-phishing, reputation, version negotiation, migration path, security considerations
- Unit tests pass:
go test ./...— 11/11 passing -
go build ./...— clean compile, zero errors - Integration: two local gateways exchange
personalmessage (Verified queue, decryptable) - Marketing with no permit → 554 rejection
- Permit issued → marketing reaches Verified queue
- Anti-phishing: display-name mismatch →
X-TFEP-Spoof-Warning+ Unknown tier - Anti-phishing:
transactionalwith no relationship token → Unknown tier - Anti-phishing: forged signature → 550 rejection
- SMTP → TFEP: plain SMTP injection → Unknown queue
- TFEP → SMTP: sent message has
X-TFEP-Proofheader +application/tfep-proofpart - PoW: gateway rejects messages missing required proof
- TLS: submission server (
:587) requires STARTTLS - Rate limit: connection flood → 421 rejection; message flood → 451 rejection
- Key rotation:
rotate-keyproduces updated DID document withnotBefore - Encrypted keys:
keygen --passphrase→ gateway loads withTFEP_KEY_PASSPHRASE - REST API:
GET /api/v1/messages,POST /api/v1/messages/{id}/release,GET /healthz - Web UI: loads at
http://localhost:8080/, shows inbox and held queue - Retention:
store vacuumdeletes old messages according to tier TTLs - Binary:
go build && ./tfep-gateway serveworks without Docker - Docker:
docker compose upruns correctly