Metadata-Version: 2.4
Name: caspian-native
Version: 0.0.7
Summary: Optional Tauri packaging for Caspian applications
Author: Caspian Native contributors
License-Expression: MIT
Keywords: caspian,pulsepoint,tauri,desktop,webview
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Win32 (MS Windows)
Classifier: Framework :: FastAPI
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: build
Requires-Dist: pyinstaller>=6; extra == "build"
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: ruff>=0.8; extra == "test"
Requires-Dist: pyright>=1.1; extra == "test"
Dynamic: license-file

# caspian-native

Optional Tauri packaging for applications built with the Caspian framework and
PulsePoint. Version `0.0.1` packages the existing application in a system
WebView; it does not translate HTML into operating-system widgets.

## Support in 0.0.1

| Target | Backend | Status |
| --- | --- | --- |
| Windows | Embedded Python sidecar | Initial support |
| Windows | Remote Caspian server | Initial support |
| Android | Remote Caspian server | Initial support |
| Android | Embedded Python | Not supported yet |

Android embedded Python is deliberately refused. A Caspian application may
depend on native Python wheels such as `asyncpg`, `cryptography`, or Pillow;
shipping an APK without proving every wheel for every ABI would produce a
package that builds unreliably or fails on launch.

## Install for development

```text
python -m pip install -e ".[test,build]"
```

Tauri's CLI is installed separately so it can match the version selected by
the generated application's `native/Cargo.toml`:

```text
cargo install tauri-cli --version "^2" --locked
```

## Add native packaging to a Caspian project

From a directory containing `caspian.config.json`:

```text
caspian-native init --identifier com.example.myapp --windows
caspian-native doctor --target windows
caspian-native backend build
caspian-native dev --target windows
caspian-native build --target windows
```

For an Android thin client backed by a deployed Caspian server:

```text
caspian-native init \
  --identifier com.example.myapp \
  --windows --android \
  --remote-url https://app.example.com
caspian-native doctor --target android
caspian-native build --target android
```

When Android is enabled, `init` also runs Tauri's non-interactive Android
project initializer. `dev` and `build` repeat this check and automatically
initialize `native/gen/android` for projects created by an earlier version.
The generated `tauri.android.conf.json` removes the desktop-only Python
sidecar from Android builds; Android always loads the configured remote server.

`init` uses Caspian's SVG icon as its vector source and generates the complete
Tauri icon set at each platform's required sizes. Re-running `init --force` also
refreshes launcher icons inside an existing generated Android Studio project.
Android receives a vector safe-zone foreground so launcher masks do not crop
the Caspian mark.

`dev --target windows` and `dev --target android` use the current project's
standard `npm run dev` stack. They reuse an already-running stack when one is
available. Otherwise, the command starts and owns it for the native session.
The native WebView loads BrowserSync's reload client, so Caspian source/public
changes use the coordinated Python restart and refresh flow without rebuilding
the embedded production sidecar. Android additionally exposes the BrowserSync
port to the connected device through `adb reverse`.
Use `--android-backend remote` to test the configured deployment instead.
Generated Android Rust targets include the linker flags needed for 16 KB page
sizes when building with Android NDK r27 or older.

The generated files live under `native/`. Re-running `init` never overwrites a
changed generated file unless `--force` is supplied.

## Configuration

`caspian.native.json` is the source of truth:

```json
{
  "$schema": "./caspian.native.schema.json",
  "schema": 1,
  "productName": "My App",
  "identifier": "com.example.myapp",
  "version": "0.0.1",
  "targets": ["windows"],
  "window": {
    "title": "My App",
    "width": 1200,
    "height": 800,
    "resizable": true
  },
  "backend": {
    "desktopMode": "embedded",
    "remoteUrl": null
  },
  "security": {
    "loopbackToken": true
  }
}
```

## Runtime model

Embedded desktop mode builds `main.py`, the Caspian runtime, project modules,
route index, and `public/` into a one-file sidecar. The sidecar:

1. restores a per-installation `AUTH_SECRET` from application data;
2. imports the project's `main.app` in production mode;
3. binds `127.0.0.1` on an operating-system-assigned port;
4. prints the protected launch URL to the Tauri host;
5. serves the normal Caspian ASGI application with Uvicorn.

The WebView receives a per-launch token once. The native middleware exchanges
it for an `HttpOnly; SameSite=Strict` cookie and refuses documents, assets,
RPCs, uploads, streams, and WebSocket handshakes without that cookie.

Caspian's production session cookie normally has the `Secure` flag. A packaged
loopback server intentionally uses `http://127.0.0.1`, so the adapter changes
only that middleware option before Starlette constructs the stack. The app
otherwise remains in production mode.

## Native bridge

Pages may capability-check `window.caspianNative`:

```javascript
const native = window.caspianNative;
if (native?.has("open-external")) {
  await native.invoke("open_external", { url: "https://example.com" });
}
```

The bridge allowlist is intentionally short: platform, application version,
application data directory, external HTTP/HTTPS/mail links, and a user-driven
file picker. An XSS can reach every bridge command, so commands must remain
safe even when the calling page is compromised.

## Current limitations

- The first embedded-backend implementation uses PyInstaller and has only been
  designed for Windows packaging.
- Dynamic route discovery requires project Python sources and
  `settings/files-list.json` to be bundled.
- Secrets from `.env` are never embedded. Remote database credentials and
  third-party credentials must be provisioned outside the package.
- Android remote mode requires connectivity and a deployed Caspian server.
- Installer signing and Android store signing are not automated in `0.0.1`.
