A Rust command-line tool for querying and managing your Android phone via the PlainApp GraphQL API.
Designed as an AI-callable CLI — feed it to any AI agent that can run shell commands to inspect and control phone data (contacts, SMS, files, notes, etc.).
PlainApp exposes a GraphQL server on your local network. Every request is end-to-end encrypted with XChaCha20-Poly1305 using a 32-byte session token.
The wire protocol (identical to plain-web):
plaintext → wrap_payload → encrypt → POST /graphql → decrypt → JSON
Payload envelope (anti-replay, matches ReplayGuard.kt):
<unix_ms> | <16-hex-nonce> | <graphql_json>
bash <(curl -fsSL https://raw.githubusercontent.com/plainhub/plainapp-cli/main/install.sh)The script auto-detects your OS/arch, downloads the right pre-built binary from
the latest GitHub Release, and
installs it to /usr/local/bin/. Override the destination with INSTALL_DIR:
INSTALL_DIR=~/.local/bin bash <(curl -fsSL .../install.sh)Prerequisites: Rust toolchain ≥ 1.75
cd plainapp-cli
cargo build --release
# binary → target/release/plainapp-cliOptionally install globally:
cargo install --path .- Open PlainApp on your Android device.
- Go to Settings → Sessions & API Tokens → "API Tokens" tab.
- Tap +, enter a name (e.g.
my-laptop), and confirm. - Note the Client ID and Token shown.
plainapp-cli --initThis creates ~/.config/plainapp-cli/config.toml with commented placeholders.
Edit it:
# Base URL of PlainApp's HTTP server on your phone (HTTP or HTTPS)
api_url = "https://192.168.1.100:8443"
# Copied from PlainApp → Settings → Sessions & API Tokens
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
token = "BASE64_ENCODED_32_BYTE_TOKEN"TLS note: PlainApp uses a self-signed certificate. The CLI accepts it automatically — no extra setup required.
Custom config path:
plainapp-cli --config /path/to/config.toml --schemaplainapp-cli [OPTIONS] <--schema | --exec <JSON> | --init>
Options:
-c, --config <FILE> Path to config file [default: ~/.config/plainapp-cli/config.toml]
-s, --schema Fetch the full GraphQL schema (introspection) as JSON
-e, --exec <JSON> Execute a GraphQL query/mutation
--init Write an example config file and exit
-h, --help Print help
-V, --version Print version
plainapp-cli --schema
# or short form:
plainapp-cli -sRuns a full GraphQL introspection query and prints the schema as pretty-printed
JSON. Pipe to jq for filtering:
plainapp-cli -s | jq '.data.__schema.types[] | select(.name | startswith("Chat"))'plainapp-cli --exec '<json>'
# or short form:
plainapp-cli -e '<json>'The <json> argument must be a JSON object with a query field (and optionally
variables).
Examples:
# List chat channels
plainapp-cli -e '{
"query": "query GetChatChannels { chatChannels { id name owner version status createdAt updatedAt members { id status } } }",
"variables": null
}'
# List notes
plainapp-cli -e '{"query":"{ app { battery sdcardPath } }","variables":null}'
# List SMS conversations
plainapp-cli -e '{"query":"{ smses(limit:20,offset:0) { id address body date } }","variables":null}'
# Delete a file
plainapp-cli -e '{
"query": "mutation DeleteFiles($paths:[String!]!) { deleteFiles(paths:$paths) }",
"variables": {"paths":["/sdcard/tmp/test.txt"]}
}'| Key | Description |
|---|---|
api_url |
Base URL, e.g. https://192.168.1.100:8443 |
client_id |
Client ID from PlainApp settings |
token |
Base64-encoded 32-byte ChaCha20 key from PlainApp settings |
The config file is TOML. You can maintain multiple files and select them with
--config.
- Never commit your config file — it contains a symmetric encryption key.
- Tokens can be revoked at any time from PlainApp → Settings → Sessions & API Tokens.
- The CLI uses
rustlswithdanger_accept_invalid_certsenabled because PlainApp generates a self-signed certificate. Only connect to trusted local networks.
cli/
├── Cargo.toml
├── README.md
└── src/
├── main.rs # CLI entry point, argument parsing
├── config.rs # Config file loading / example generation
├── crypto.rs # XChaCha20-Poly1305 encrypt / decrypt + payload wrapping
└── api.rs # HTTP client, GraphQL introspection query