CASE_01 · 2024–present · TIKTOK_USDS_JV
TikTok · USDS JV · Custom SDK · iOS/Android/macOS
I lead the architecture and evolution of TikTok USDS JV's custom mobile security SDK across Android, iOS, and macOS — shared C++ core, encrypted JNI bridge, ARM64/x86-64 hardening at the assembly level. The work modernized the cross-platform core and migrated us off commercial obfuscators — technical sovereignty over a security stack running on the TikTok app at an average 1.9B DAU.
scale
average DAU running the SDK
// case_study
I joined TikTok USDS JV in 2024 as the Anti-Automation Mobile Security Lead. My mandate was to lead the architecture and evolution of the custom security SDK across Android, iOS, and macOS — modernize the cross-platform core and execute a strategic migration off the commercial obfuscators we'd outgrown, into a custom-engineered assembly-level hardening pipeline we own end-to-end.
At an average 1.9B DAU on the SDK, technical sovereignty over the security stack isn't a cost decision — it's a scale decision. Commercial obfuscators are calibrated for standard apps; at this scale and threat model they run out of room. The constraints on the work are: don't slow the app down, don't show your hand, don't trust the device.
The SDK is structured around a shared C++ security core. Same logic, three platforms — same detection policy, same risk classification, same JNI/FFI bridge encryption — so an attacker who finds a gap in one platform doesn't find a gap in all three. Each platform compiles a thin host layer that exposes the core to the local conventions: NDK on Android, Objective-C++ on iOS, the macOS lifecycle on macOS.
My architectural authority lives in the bridges between them — the platform parity, the build guardrails (ASan gates, CI / release pipelines), and the US/CN data isolation that has to hold all the way through. A detection policy expressed once in the C++ core has to translate identically onto every platform's wire; that's a parity problem disguised as a security problem, and it's where most of the architectural work actually goes.
Every new module follows the same architectural decisions — feature-based modularization, single-responsibility, clean architecture inside the SDK. Given the nature of an SDK, non-blocking async and performance scenarios come first; the caller should feel almost-zero latency and overhead from us. The internal monitoring dashboard is a Jetpack Compose surface fed by Kotlin Flow, which gives us baseline verification and stress-test signal in real time.
Anti-RE at the assembly level. NDK obfuscation, symbol stripping, JNI bridge encryption, ARM64 and x86-64 hardening at the instruction level — all of it custom, all of it ours. AddressSanitizer (ASan) gates Android and macOS builds to keep memory-safety honest. We run adversarial audits against our own SDK to validate the countermeasures hold against active reverse engineering — the test of a security SDK isn't the static analysis report, it's the day someone actually tries.
ZeroTrust without root. We don't ask for root, ever. The client builds a risk classification of shared-infrastructure patterns from observation alone — VPN concentrators, proxy clusters, device farms — without requiring privileged device access. The signal is extracted client-side; the policy decision happens server-side; the device never grants us anything it shouldn't.
Behavioral analysis at the scale we have. SQL/Hive on millions of device sessions per window, looking for automation patterns. The SDK emits structured telemetry; the analysis pipeline pulls fraud signal out of it; signal feeds back into client policy.
US/CN data isolation. Geographic data isolation is codified into the build, not bolted on after. The C++ core, the platform host layers, the cross-team handoff between Android, iOS, and macOS — all of it has to respect the boundary all the time.
Lifecycle on macOS. Process termination detection and library cleanup on macOS were unique to that platform. Implementing reliable lifecycle hooks for an SDK on macOS was a small project of its own inside the bigger one.
Architectural authority doesn't scale linearly. I can't be in every code review, every onboarding, every C++ style decision — and I shouldn't be. The "Interactive Architect" agent codifies the architectural decisions that took us a year to land — module discovery, build process, the why behind the why — so a new hire is productive inside a sprint instead of a quarter.
Adjacent agents bridge the Security and Development gap by automating multi-step workflows: risk-label retrieval, integrated security reporting, C++ style and STL convention enforcement, the US/CN isolation checks. The AI isn't the security work; it's how the standards I set actually hold across an org without bottle-necking on me.
modern stack
Kotlin · Jetpack Compose · Kotlin Flow · Java · Android SDK
systems & security
NDK · C/C++ · JNI · ARM64 / x86-64 assembly · iOS · macOS · Protobuf · gRPC · Cronet · SQL/Hive · Python · REST APIs