Four ways to persist data, in rough order of how often you should reach for them: the key-value store for small state, scoped file I/O for the app's own files, shared file I/O for files other apps may read, and full file access for anything else on the card — the only one that needs a capability. IPC delivers short text messages between apps.

The sandbox is free.

Everything on this page except full file access is ungated: no manifest declaration, no consent prompt. Scoping is enforced by path construction, so an app physically cannot name a file outside its own directory.

Scoped file I/O

These calls are sandboxed to the app's own directory: /sd/apps/<app_id>/. Paths are relative — a path of "data/notes.txt" accesses /sd/apps/<app_id>/data/notes.txt. Parent directory traversal (../) is rejected.

Capability: None

file_read

Read the entire contents of a file into memory.

jpp_sdk_status_t jpp_sdk_file_read(jpp_sdk_context_t *ctx,
                                    const char *relative_path,
                                    jpp_broker_result_t *result);
jppsdk.file_read(path: str) -> dict

Returns: - C: JPP_SDK_OK; result->text is the file contents as a null-terminated string. - Python: {"text": str} — access the contents with result["text"].


file_write

Write a string to a file, replacing its contents. Creates the file (and any parent directories) if necessary.

jpp_sdk_status_t jpp_sdk_file_write(jpp_sdk_context_t *ctx,
                                     const char *relative_path,
                                     const char *text,
                                     jpp_broker_result_t *result);
jppsdk.file_write(path: str, text: str) -> None

Notes: Writing is atomic via a temp-file rename — a power loss during the write will not corrupt the existing file.


file_list

List the contents of a directory.

jpp_sdk_status_t jpp_sdk_file_list(jpp_sdk_context_t *ctx,
                                    const char *relative_dir,
                                    jpp_broker_result_t *result);
jppsdk.file_list(dir: str) -> dict

Returns: - C: JPP_SDK_OK; result->text is a newline-separated list of filenames. Individual entries are also in result as entry_0, entry_1, etc. - Python: {"text": str} — split on "\n" to get individual names.


Shared file I/O

These calls work identically to scoped file I/O but target /sd/shared/<app_id>/ instead. Files here can be seen and read by other apps that specifically open that path with files.full.

Capability: None

jpp_sdk_status_t jpp_sdk_shared_read(jpp_sdk_context_t *ctx,
                                      const char *relative_path,
                                      jpp_broker_result_t *result);

jpp_sdk_status_t jpp_sdk_shared_write(jpp_sdk_context_t *ctx,
                                       const char *relative_path,
                                       const char *text,
                                       jpp_broker_result_t *result);

jpp_sdk_status_t jpp_sdk_shared_list(jpp_sdk_context_t *ctx,
                                      const char *relative_dir,
                                      jpp_broker_result_t *result);
jppsdk.shared_read(path: str) -> dict
jppsdk.shared_write(path: str, text: str) -> None
jppsdk.shared_list(dir: str) -> dict

Full file access

These calls allow access to any path on the SD card under /sd/. Each path the app opens requires individual user approval — a Deny/Allow prompt appears before the handle is granted.

Capability: files.full — plus a prompt per path.

Tier 2 — prompted on first use of every launch, never persisted. On top of that, every individual path the app opens raises its own Deny/Allow prompt, which is likewise never persisted. The capability answers "may this app touch the filesystem at all"; the path prompt answers "which file", which is the question a user can actually judge.

file_open

Open a file and obtain a handle. A user approval prompt is shown for the requested path and mode before the handle is granted.

jpp_sdk_status_t jpp_sdk_file_open(jpp_sdk_context_t *ctx,
                                    const char *path,
                                    jpp_sdk_open_mode_t mode,
                                    jpp_sdk_handle_t *out_handle);
jppsdk.file_open(path: str, mode: int) -> int

Parameters:

Name Description
path Absolute path under /sd/, e.g. "/sd/shared/other_app/data.txt".
mode OPEN_READ (0) or OPEN_WRITE (1).
out_handle / return An integer handle used in subsequent handle_* calls.

Limits: Maximum 4 open handles per session. Returns JPP_SDK_HANDLE_LIMIT if exceeded.


handle_read

Read the entire contents of an open file handle.

jpp_sdk_status_t jpp_sdk_handle_read(jpp_sdk_context_t *ctx,
                                      jpp_sdk_handle_t handle,
                                      jpp_broker_result_t *result);
jppsdk.handle_read(handle: int) -> dict

Returns: result->text / result["text"] — the file contents.


handle_write

Write a string to an open file handle (must be opened with OPEN_WRITE).

jpp_sdk_status_t jpp_sdk_handle_write(jpp_sdk_context_t *ctx,
                                       jpp_sdk_handle_t handle,
                                       const char *text,
                                       jpp_broker_result_t *result);
jppsdk.handle_write(handle: int, text: str) -> None

handle_list

List directory contents for a handle opened on a directory path.

jpp_sdk_status_t jpp_sdk_handle_list(jpp_sdk_context_t *ctx,
                                      jpp_sdk_handle_t handle,
                                      jpp_broker_result_t *result);
jppsdk.handle_list(handle: int) -> dict

handle_close

Close an open handle and release the slot.

jpp_sdk_status_t jpp_sdk_handle_close(jpp_sdk_context_t *ctx,
                                       jpp_sdk_handle_t handle);
jppsdk.handle_close(handle: int) -> None

Notes: All open handles are automatically closed when the app exits. Close handles promptly — the limit is 4 per session.


Key-value store

A simple persistent key-value store backed by .kv.json in the app's scoped directory (/sd/apps/<app_id>/.kv.json). Survives app restarts; suitable for preferences, scores, cached data.

Capability: None

kv_get

Return the value for a key, or None/JPP_SDK_NO_DATA if the key does not exist.

jpp_sdk_status_t jpp_sdk_kv_get(jpp_sdk_context_t *ctx,
                                 const char *key,
                                 char *out_value,
                                 size_t value_buf_len);
jppsdk.kv_get(key: str) -> str | None

Parameters:

Name Limit
Key 64 characters
Value 256 characters

Returns: - C: JPP_SDK_OK if the key exists, JPP_SDK_NO_DATA if absent. - Python: the value string, or None if the key does not exist (does not raise).


kv_set

Set or update a key.

jpp_sdk_status_t jpp_sdk_kv_set(jpp_sdk_context_t *ctx,
                                 const char *key,
                                 const char *value);
jppsdk.kv_set(key: str, value: str) -> None

kv_delete

Delete a key. No-op if the key does not exist.

jpp_sdk_status_t jpp_sdk_kv_delete(jpp_sdk_context_t *ctx,
                                    const char *key);
jppsdk.kv_delete(key: str) -> None

IPC

Apps can send and receive text messages through file-backed mailboxes at /sd/ipc/<recipient>/<sender>/. Messages are consumed and deleted on receive.

Capability: None

ipc_send

Send a message to another app's mailbox.

jpp_sdk_status_t jpp_sdk_ipc_send(jpp_sdk_context_t *ctx,
                                   const char *recipient_app_id,
                                   const char *payload,
                                   jpp_broker_result_t *result);
jppsdk.ipc_send(recipient_id: str, payload: str) -> None

Parameters:

Name Description
recipient_app_id The app_id of the target app. The target does not need to be running.
payload Message body. Maximum 512 bytes.

ipc_recv

Check this app's mailbox and return the oldest unread message. Non-blocking.

jpp_sdk_status_t jpp_sdk_ipc_recv(jpp_sdk_context_t *ctx,
                                   char *out_payload,
                                   size_t payload_buf_len,
                                   jpp_broker_result_t *result);
jppsdk.ipc_recv() -> tuple[str, str] | None

Returns: - C: JPP_SDK_OK if a message was read; JPP_SDK_NO_DATA if the mailbox is empty. When OK, result->text is the payload and result->sender is the sender's app_id. - Python: (payload, sender_app_id) tuple, or None if no messages.

Notes: Messages are deleted from the mailbox on receipt. Poll ipc_recv() in on_idle to process incoming messages.