Protocol overview
The Tweaks module exposes HTTP over an app-local abstract
Unix-domain socket. Snap-O, custom clients, and authorized
agents connect through ADB forwarding. The server does not
listen on an Android TCP port or a public network interface,
and does not require the INTERNET permission.
serial=emulator-5554
socket=snapo_tweaks_12345
adb -s "$serial" shell cat /proc/net/unix
port="$(adb -s "$serial" forward tcp:0 "localabstract:$socket")"
base="http://127.0.0.1:$port"
curl -fsS "$base/tweaks"
Socket names follow snapo_tweaks_<pid>.
Keep the forward alive while your client uses it, and
discover and forward the new socket again when the Android
app process restarts. When finished, remove only the forward
you created:
adb -s "$serial" forward --remove "tcp:$port"
The Snap-O CLI manages this discovery and cleanup
automatically. For a remote ADB server, an ADB forward is
local to the server’s host; tunnel it to your client, or use
the CLI with both --adb-host and
--adb-port for direct ADB transport.
Debug builds enable the server by default. A nondebuggable app
can enable it only by including the real Tweaks dependency and
setting snapo.tweaks.allow_release to
true in its application's
<application> metadata. Release no-op
artifacts remain the recommended default. See
release setup for the
manifest example. Network Inspector has its own independent
snapo.network.allow_release opt-in.
When its optional dependency is installed and its developer setting is enabled, the on-device floating panel observes the same tweak registry directly. Changes from the panel, Snap-O, and custom clients update that shared registry without a separate panel-specific HTTP connection. Host applications observe updates through the event stream or their normal refresh behavior. See the floating overlay setup for Android integration.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/app |
Read the app name and package name. |
GET |
/app/icon |
Read the app icon. |
GET |
/tweaks |
List currently composed tweaks and descriptors. |
PATCH |
/tweaks |
Update or reset one or more live values. |
GET |
/tweaks/events |
Subscribe to live tweak snapshots. |
Composition determines visibility. A tweak can be listed, changed, or streamed only while at least one composable using its name is in composition. Its last edited value is retained within the app process and restored when the same declaration returns.
GET /app
Read the running app’s user-visible name and Android package name. Use this endpoint to identify which app a client has connected to.
GET /app HTTP/1.1
Host: 127.0.0.1
{
"name": "Snap-O Tweaks Demo",
"packageName": "com.openai.snapo.demo.tweaks"
}
GET /app/icon
Read the running app’s icon. Use the response’s
Content-Type without assuming a particular
image format or size.
GET /app/icon HTTP/1.1
Host: 127.0.0.1
HTTP/1.1 200 OK
Content-Type: <image media type>
<image bytes>
The server returns 404 Not Found if an app icon
cannot be loaded.
GET /tweaks
List the controls currently in composition. Each descriptor
includes its full name, value type, default, and current
value. Numeric controls also include any configured
min, max, and step.
GET /tweaks HTTP/1.1
Host: 127.0.0.1
{
"tweaks": [
{
"name": "Typography/Font size",
"type": "int",
"default": 36,
"value": 36,
"min": 16,
"max": 72,
"step": 1
},
{
"name": "Colors/Text",
"type": "color",
"default": "#18212F",
"value": "#18212F"
},
{
"name": "Motion/Show",
"type": "boolean",
"default": true,
"value": true
}
]
}
Supported type values are int,
float, boolean,
color, and string. Colors use
#RRGGBB or #RRGGBBAA.
Compose color defaults retain their original color space and
precision in the app, while HTTP represents them as sRGB
hex. Color.Unspecified appears as
#00000000. Updating a color with its reported
default restores the original Compose value, including when
that value does not round-trip through its sRGB hex form.
Integer values must be whole numbers. Floating-point values
may be whole or fractional. When present, a numeric
step is relative to min, or to the
tweak's default when no minimum is specified.
Reusing a name shares one value across active composables;
its type, default, and constraints must match everywhere.
PATCH /tweaks
Update one or more registered tweaks in a single request.
Send application/json and put the complete tweak
names and their new primitive values in a
values object.
PATCH /tweaks HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Content-Length: 78
{
"values": {
"Typography/Font size": 48,
"Motion/Show": false
}
}
{
"tweaks": [
{
"name": "Typography/Font size",
"value": 48
},
{
"name": "Motion/Show",
"value": false
}
]
}
Updates are validated together. Every tweak must exist, match its declared type, and satisfy its numeric bounds and step before any value is changed. A successful response includes only the updated names and values.
Reset to the default
There is no separate reset endpoint. Send the
default reported by GET /tweaks
through the same update request.
{
"values": {
"Typography/Font size": 36,
"Motion/Show": true
}
}
GET /tweaks/events
Subscribe to the current full tweak snapshot and subsequent
changes as controls are registered, removed, or updated.
The response uses server-sent events with the
text/event-stream content type.
GET /tweaks/events HTTP/1.1
Host: 127.0.0.1
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache
event: tweaks
data: {"tweaks":[{"name":"Typography/Font size","type":"int","default":36,"value":36,"min":16,"max":72,"step":1}]}
event: tweaks
data: {"tweaks":[{"name":"Typography/Font size","type":"int","default":36,"value":48,"min":16,"max":72,"step":1}]}
: keep-alive
Each tweaks event contains a complete current
snapshot, not a patch. Replace your client’s current control
list with the received tweaks array. Lines
starting with : are keep-alive comments.
The first event arrives immediately. Later changes made within the same Android main-thread turn are combined into one ordered snapshot, including values changed by the on-device overlay. An omitted tweak has left composition. Slow clients receive the newest complete snapshot instead of accumulating every intermediate state.
Browser clients need a same-origin proxy.
The Android server does not provide CORS headers or handle
browser OPTIONS preflight. Serve your interface
and its forwarded API through the same origin; native and
server-side HTTP clients do not have this restriction.
Error responses
Errors return a JSON object containing an
error message and the relevant HTTP status.
{
"error": "Invalid value for Typography/Font size: Value exceeds the maximum."
}
| Status | Meaning |
|---|---|
400 Bad Request |
Malformed request, invalid JSON, or a missing values object. |
404 Not Found |
Unknown endpoint, unavailable icon, or tweak not currently in composition. |
405 Method Not Allowed |
Unsupported endpoint method. The Allow response header lists valid methods. |
408 Request Timeout |
The request did not complete within the server’s timeout. |
413 Payload Too Large |
The JSON request exceeds the server’s maximum request size. |
422 Unprocessable Entity |
A value has the wrong type, invalid color, or violates its bounds or step. |
500 Internal Server Error |
An unexpected failure occurred while processing a tweak update. |
503 Service Unavailable |
The connection limit was reached, or the Android main thread is unavailable. |
504 Gateway Timeout |
A tweak update timed out waiting for the Android main thread. |