Every app has two faces.
The one your users see — polished, intentional, pixel-perfect. And the one underneath — network calls, logs, flags, navigation, state.
That second face is where bugs live. That second face is what QA sees. That second face is what you debug every day.
Unveil makes it visible.
A single swipe. Everything is there.
And when you're done — it's gone.
- 💥 Trigger crashes on demand
- 📱 Device and environment info
- 📋 Stream and filter logs
- 🧭 Visualize navigation state
- 🌐 Inspect network requests in real time
All inside your app. No external tools. No rebuilds.
Unveil.configure {
register(NetworkPlugin(...))
register(LogPlugin(...))
}
if (BuildConfig.DEBUG) Unveil.enable()
@Composable
fun App() {
UnveilHost(enabled = Unveil.isEnabled) {
AppNavHost()
}
}That’s it.
Unveil is not a tool.
It’s a platform for tools.
Everything is a plugin.
Unveil.configure {
register(NetworkPlugin(...))
register(LogPlugin(...))
}And when you need flexibility:
Unveil.register(SomePlugin())No coupling. No assumptions. Just composition.
-
Zero impact on your app
UnveilHostwraps your UI. Nothing else changes. -
Zero overhead when disabled No UI. No gestures. No background work.
-
Zero dependency on your stack No Material. No DI. No navigation library.
-
Fully modular Core knows nothing about features.
Unveil is modular.
Start with the core, then add only the plugins you need.
[versions]
unveil = "current_version"
[libraries]
unveil-core = { module = "me.passos.libs.unveil:unveil-core", version.ref = "unveil" }commonMain.dependencies {
implementation(libs.unveil.core)
}Pick the features you need:
unveil-crash = { module = "me.passos.libs.unveil:unveil-crash", version.ref = "unveil" }
unveil-deviceinfo = { module = "me.passos.libs.unveil:unveil-deviceinfo", version.ref = "unveil" }
unveil-logs = { module = "me.passos.libs.unveil:unveil-logs", version.ref = "unveil" }
unveil-navigation = { module = "me.passos.libs.unveil:unveil-navigation", version.ref = "unveil" }
unveil-network = { module = "me.passos.libs.unveil:unveil-network", version.ref = "unveil" }commonMain.dependencies {
implementation(libs.unveil.logs)
implementation(libs.unveil.network)
}Adapters connect Unveil to your stack:
unveil-logs-kermit = { module = "me.passos.libs.unveil:unveil-logs-kermit", version.ref = "unveil" }
unveil-network-ktor = { module = "me.passos.libs.unveil:unveil-network-ktor", version.ref = "unveil" }
unveil-navigation-compose = { module = "me.passos.libs.unveil:unveil-navigation-compose", version.ref = "unveil" }Want everything?
unveil-core = { module = "me.passos.libs.unveil:unveil-core", version.ref = "unveil" }
unveil-crash = { module = "me.passos.libs.unveil:unveil-crash", version.ref = "unveil" }
unveil-deviceinfo = { module = "me.passos.libs.unveil:unveil-deviceinfo", version.ref = "unveil" }
unveil-logs = { module = "me.passos.libs.unveil:unveil-logs", version.ref = "unveil" }
unveil-logs-kermit = { module = "me.passos.libs.unveil:unveil-logs-kermit", version.ref = "unveil" }
unveil-navigation = { module = "me.passos.libs.unveil:unveil-navigation", version.ref = "unveil" }
unveil-navigation-compose = { module = "me.passos.libs.unveil:unveil-navigation-compose", version.ref = "unveil" }
unveil-network = { module = "me.passos.libs.unveil:unveil-network", version.ref = "unveil" }
unveil-network-ktor = { module = "me.passos.libs.unveil:unveil-network-ktor", version.ref = "unveil" }| Category | Modules |
|---|---|
| Core | unveil-core |
| Crash | unveil-crash |
| Device | unveil-deviceinfo |
| Logs | unveil-logs, unveil-logs-kermit |
| Navigation | unveil-navigation, unveil-navigation-compose |
| Network | unveil-network, unveil-network-ktor |
unveil-core requires:
compose.uicompose.animationcompose.material3androidx.activity:activity-compose(Android only)
class MyPlugin : UnveilPlugin {
override val id = "my_plugin"
override val title = "My Plugin"
override val icon = UnveilIcon.Emoji("🔧")
@Composable
override fun Content(scope: UnveilPanelScope) {
// Build anything you want
}
}Unveil is stack-agnostic.
Each integration point is defined as an interface. You can use a built-in adapter or provide your own implementation.
| Integration | Interface | Adapter |
|---|---|---|
| Logs | LogSink |
Kermit |
| Network | NetworkInterceptor |
Ktor |
| Navigation | NavigationObserver |
Compose Navigation |
Using OkHttp? Timber? Voyager? Implement the interface — it’s a handful of methods.
| Platform | Support |
|---|---|
| Android | ✅ |
| iOS | ✅ |
| Desktop | 🔲 |
Unveil is not a debug panel.
It’s observability inside your UI.
Apache 2.0