This document outlines the security architecture, threat model, and cryptographic controls implemented across TapBot (Bot Store & On-Device Telegram Bot Runner).
CRITICAL ACCEPTANCE CRITERION
A Telegram bot token or API secret entered by a user NEVER leaves the Android device.
Secrets are strictly stored in hardware-backed storage and are never transmitted to Cloudflare Workers, Cloudflare D1, Cloudflare R2, or any remote server controlled by TapBot.
+-------------------------------------------------------------------------+
| ANDROID DEVICE |
| |
| +--------------------+ Secure Input +--------------------+ |
| | Credential UI | --------------------> | Android Keystore | |
| | (FLAG_SECURE ON) | | (AES-256-GCM) | |
| +--------------------+ +--------------------+ |
| | | |
| | (NEVER SENT) | |
| x v |
| x +--------------------+ |
| x | Local Bot Runner | |
| x | (Isolated Loop) | |
| x +--------------------+ |
| x | |
+-------------x--------------------------------------------|--------------+
x | HTTPS
x v
+-----------------------------+ +--------------------+
| TapBot Cloudflare Backend | | Telegram Bot API |
| (Catalog & Metadata ONLY) | | (Direct from phone)|
+-----------------------------+ +--------------------+
The TapBot backend only serves public catalog metadata, versions, changelogs, and package binaries. The backend does not maintain user accounts, user database tables, or credential endpoints.
- Threat: An attacker replaces legitimate
.botpkgdistribution packages with compromised code intended to exfiltrate tokens or execute malicious payloads. - Mitigation:
- Cryptographic Hash Verification: Every package release is registered in Cloudflare D1 with a SHA-256 checksum calculated at build time.
- Streaming Integrity Verification: During download,
Sha256PackageVerifierstreams the bytes throughMessageDigest("SHA-256"). If the computed digest does not match the metadata checksum byte-for-byte, the file is rejected immediately and deleted from disk before any installation or execution can occur. - Future Digital Signing: The architecture is designed with the
DigitalSignatureVerifierhook, allowing packages to be verified against the publisher's Ed25519/RSA-PSS public key.
- Threat: The storage CDN hosting
.botpkgbinaries is breached, and packages are altered or swapped directly on the CDN. - Mitigation:
- Packages fetched from the CDN are checked against the SHA-256 digest retrieved from the catalog API database.
- Because the download verification is performed on the Android client before local decompression, tampered binaries on the CDN will fail the client-side hash check and be purged immediately.
- Threat: An attacker gains full administrative access to the TapBot Cloudflare Worker environment and D1 SQLite database.
- Impact & Mitigation:
- Zero Token Exposure: Since user credentials are never transmitted or stored on the backend, a full breach of the database and worker environment yields zero user bot tokens or user secrets.
- Attacker Attempting to Push Malicious Hashes: If an attacker alters the SHA-256 in D1 to match a malicious binary on R2, client-side downgrade attack checks (
DowngradeAttackChecker) prevent rolling back to compromised older versions. Furthermore, package signing (Ed25519) will ensure that only packages signed with the offline publisher key are accepted.
- Threat: Tokens are intercepted via unencrypted network transit, exported Android IPC components, or ambient memory leaks.
- Mitigation:
- Local Injection: Bot credentials are read from
CredentialStoreand injected directly into the local runtime process/coroutine environment. - Android Component Security:
BotForegroundServiceis configured withandroid:exported="false"inAndroidManifest.xml. External applications cannot bind to or command the service. - Direct TLS to Telegram: Outbound bot traffic goes directly from the local device to
https://api.telegram.orgover TLS 1.3.
- Local Injection: Bot credentials are read from
- Threat: Sensitive tokens and API keys are printed in Android logcat, crash traces, analytics, or UI console panes.
-
Mitigation:
-
SecretRedactorEngine: All system and bot runtime logs pass throughSecretRedactorbefore being written to disk or shown in the UI:- Telegram Bot Tokens (
\d{8,12}:[a-zA-Z0-9_-]{35,50})$\rightarrow$ [REDACTED_TELEGRAM_TOKEN] - Telegram API URLs (
/bot<token>/...)$\rightarrow$ /bot[REDACTED_TELEGRAM_TOKEN]/... - OpenAI Keys (
sk-[a-zA-Z0-9_-]{20,})$\rightarrow$ [REDACTED_OPENAI_KEY] - Anthropic Keys (
sk-ant-[a-zA-Z0-9_-]{20,})$\rightarrow$ [REDACTED_ANTHROPIC_KEY] - Bearer Authorization Headers
$\rightarrow$ Bearer [REDACTED_BEARER_TOKEN] - Dynamic In-Memory Secret Registry: Any custom secret saved by the user is registered in memory with
SecretRedactor.registerSecret(value)and scrubbed with[REDACTED_SECRET].
- Telegram Bot Tokens (
-
- Threat: An attacker gains access to physical device storage, extracts ADB backups, or accesses the application sandbox on a rooted device.
- Mitigation:
- Hardware-Backed Keystore: Credentials are encrypted using
EncryptedSharedPreferencesbacked by Android Keystore'sMasterKey(AES256_GCM_SPEC). - Key Derivation in Hardware: The master key is generated inside the device's Trusted Execution Environment (TEE) or StrongBox Keymaster. The raw cryptographic keys never leave the hardware module.
- Backup Disabled:
android:allowBackup="false"is set inAndroidManifest.xmlto prevent credential extraction viaadb backup.
- Hardware-Backed Keystore: Credentials are encrypted using
- Threat: An attacker tricks the app into "upgrading" a bot with an older version that has known security vulnerabilities.
- Mitigation:
DowngradeAttackChecker: Validates incoming package versions against currently installed versions using Semantic Versioning (SemVer:MAJOR.MINOR.PATCH).- If the incoming package version is lower than the installed version, the installation is aborted with
SecurityException("Downgrade attack detected").
- Threat: Background spyware captures screenshots of the credential input field, or the OS task switcher displays a plaintext preview of the user's Telegram token.
- Mitigation:
FLAG_SECUREEnforced:SecurityWindowManager.setSecureFlag(activity, true)setsWindowManager.LayoutParams.FLAG_SECUREwhenever theBotDetailScreenis active.- Screenshots, screen recordings, and OS recent app thumbnail captures are blocked by the Android window compositor.
The primary credential storage interface is CredentialStore:
interface CredentialStore {
suspend fun saveCredential(botId: String, key: String, secretValue: String)
suspend fun getCredential(botId: String, key: String): String?
suspend fun deleteCredential(botId: String, key: String)
suspend fun hasCredential(botId: String, key: String): Boolean
suspend fun getAllCredentials(botId: String): Map<String, String>
suspend fun deleteCredentials(botId: String)
suspend fun hasRequiredCredentials(botId: String, requiredKeys: List<String>): Boolean
}The production implementation KeystoreCredentialStore creates an isolated EncryptedSharedPreferences container per bot (tapbot_secure_creds_${botId}) encrypted with AES-256-GCM.
The security guarantees are verified by the automated test suite:
| Test Class | Scope |
|---|---|
BotCredentialSecurityTest.kt |
Verifies user tokens never reach backend, missing credentials prevent start, deletion wipes storage, logs are redacted |
CredentialStoreTest.kt |
Unit tests for Keystore credential storage lifecycle and isolation |
SecretRedactorTest.kt |
Unit tests verifying regex scrubbing of Telegram tokens, API keys, and registered secrets |
PackageVerifierTest.kt |
Streaming SHA-256 validation, corrupt package deletion, downgrade protection |
- Bots must NEVER accept hardcoded tokens or secrets in package assets or code.
- Bots must declare all required credentials in
manifest.jsonunder thecredentialsarray withsecret: true. - Bots must read credentials exclusively from environment variables or runtime configuration files supplied by the local runner.
- Bots must NEVER transmit user credentials to external analytics or telemetry services.