# Milestone 4: App Sandboxing

**Status**: Not Started
**Goal**: Secure app runtime with defined package format and permission system.

---

## Overview

Apps in Mosis need:
- Isolation from each other and the system
- Defined package format for distribution
- Permission model for hardware/data access
- Lifecycle management

---

## Runtime Architecture

### Recommendation: Hybrid Approach

| Component | Runtime | Reason |
|-----------|---------|--------|
| UI Scripts | Lua | Native RmlUi integration, simple |
| App Logic | Lua (now), WASM (future) | Start simple, add WASM for isolation |
| System Services | C++ | Performance, direct hardware access |

### Lua Sandbox

RmlUi already uses Lua for UI scripting. We enhance it with:

```lua
-- Sandboxed globals per app
app = {
    id = "com.example.myapp",
    storage = AppStorage("com.example.myapp"),
    permissions = {"camera", "storage"},
}

-- Restricted stdlib
-- Remove: os.execute, io.popen, loadfile, dofile
-- Keep: string, table, math, coroutine
```

**Sandbox Implementation** (`src/main/kernel/src/lua_sandbox.cpp`):

```cpp
class LuaSandbox {
public:
    lua_State* CreateAppState(const std::string& app_id,
                               const std::vector<std::string>& permissions);

    void RestrictGlobals(lua_State* L);
    void InjectAppAPIs(lua_State* L, const AppManifest& manifest);

private:
    void RemoveDangerousFunctions(lua_State* L);
    void SetupPermissionChecks(lua_State* L);
};
```

---

## Package Format (.mpkg)

### Directory Structure

```
myapp.mpkg/
├── manifest.json        # Required: metadata, permissions
├── ui/
│   ├── main.rml         # Entry point
│   ├── screens/         # Additional screens
│   │   └── settings.rml
│   ├── styles/
│   │   └── app.rcss
│   └── scripts/
│       └── app.lua
├── assets/
│   ├── icon.png         # 48x48 app icon
│   ├── icon_large.png   # 192x192 for store
│   └── images/
└── locales/             # Optional: i18n
    ├── en.json
    └── es.json
```

### Manifest Schema

**File**: `manifest.json`

```json
{
  "$schema": "https://mosis.dev/schemas/manifest-v1.json",

  "id": "com.example.myapp",
  "name": "My App",
  "version": "1.0.0",
  "version_code": 1,

  "description": "A sample Mosis app",
  "author": "Developer Name",
  "website": "https://example.com",

  "entry": "ui/main.rml",
  "icon": "assets/icon.png",

  "permissions": [
    "camera",
    "microphone",
    "storage",
    "network",
    "contacts.read",
    "contacts.write"
  ],

  "min_mosis_version": "1.0.0",

  "intents": {
    "share": {
      "types": ["image/*", "text/plain"],
      "action": "ui/share.rml"
    }
  }
}
```

### Manifest TypeScript Interface (for validation)

```typescript
interface MosisManifest {
  id: string;           // Reverse domain notation
  name: string;         // Display name
  version: string;      // SemVer
  version_code: number; // Incremental integer

  description?: string;
  author?: string;
  website?: string;

  entry: string;        // Path to main RML
  icon: string;         // Path to icon

  permissions: Permission[];
  min_mosis_version?: string;

  intents?: Record<string, Intent>;
}

type Permission =
  | "camera"
  | "microphone"
  | "speaker"
  | "storage"
  | "network"
  | "contacts.read"
  | "contacts.write"
  | "messages.read"
  | "messages.write"
  | "location"
  | "phone.call"
  | "notifications";
```

---

## Permission System

### Permission Levels

| Level | Description | Example |
|-------|-------------|---------|
| Normal | Auto-granted | `storage` (app's own data) |
| Dangerous | User prompt required | `camera`, `contacts.read` |
| Signature | System apps only | `phone.call`, `system.settings` |

### Permission Request Flow

```
App declares permission in manifest
           ↓
User installs app
           ↓
On first use of protected API:
           ↓
┌─────────────────────────────────┐
│ "My App" wants to access your   │
│ camera. Allow?                  │
│                                 │
│  [Deny]  [Allow Once]  [Allow]  │
└─────────────────────────────────┘
           ↓
Decision stored in PermissionManager
           ↓
API call proceeds or fails
```

### Permission Manager

**File**: `src/main/kernel/include/permission_manager.h`

```cpp
namespace mosis {

enum class PermissionStatus {
    NotRequested,
    Granted,
    Denied,
    AllowedOnce
};

class IPermissionManager {
public:
    virtual ~IPermissionManager() = default;

    // Check if app has permission
    virtual PermissionStatus Check(const std::string& app_id,
                                    const std::string& permission) = 0;

    // Request permission (may show UI)
    using PermissionCallback = std::function<void(PermissionStatus)>;
    virtual void Request(const std::string& app_id,
                         const std::string& permission,
                         PermissionCallback callback) = 0;

    // Revoke permission
    virtual void Revoke(const std::string& app_id,
                        const std::string& permission) = 0;

    // Get all permissions for app
    virtual std::map<std::string, PermissionStatus>
        GetAppPermissions(const std::string& app_id) = 0;
};

} // namespace mosis
```

---

## App Lifecycle

### States

```
INSTALLED → LAUNCHING → RUNNING → PAUSED → STOPPED → UNINSTALLED
    ↑                      ↓
    └──────────────────────┘
         (resume)
```

### Lifecycle Events

```lua
-- In app.lua
function onAppCreate()
    -- Initialize app state
end

function onAppResume()
    -- Returning from background
end

function onAppPause()
    -- Going to background, save state
end

function onAppDestroy()
    -- Cleanup
end
```

### App Manager

**File**: `src/main/kernel/include/app_manager.h`

```cpp
namespace mosis {

struct InstalledApp {
    std::string id;
    std::string name;
    std::string version;
    std::string icon_path;
    std::string install_path;
    int64_t installed_time;
};

class IAppManager {
public:
    virtual ~IAppManager() = default;

    // Installation
    virtual bool Install(const std::string& mpkg_path) = 0;
    virtual bool Uninstall(const std::string& app_id) = 0;
    virtual bool Update(const std::string& mpkg_path) = 0;

    // Query
    virtual std::vector<InstalledApp> GetInstalledApps() = 0;
    virtual std::optional<InstalledApp> GetApp(const std::string& app_id) = 0;

    // Lifecycle
    virtual bool Launch(const std::string& app_id) = 0;
    virtual bool Stop(const std::string& app_id) = 0;
    virtual bool IsRunning(const std::string& app_id) = 0;

    // Inter-app communication
    virtual void SendIntent(const std::string& action,
                           const std::map<std::string, std::string>& data) = 0;
};

} // namespace mosis
```

---

## Storage Isolation

### Per-App Storage

```cpp
class AppStorage {
public:
    AppStorage(const std::string& app_id);

    // Key-value storage (like SharedPreferences)
    void SetString(const std::string& key, const std::string& value);
    std::string GetString(const std::string& key, const std::string& default_value = "");
    void SetInt(const std::string& key, int value);
    int GetInt(const std::string& key, int default_value = 0);
    void SetBool(const std::string& key, bool value);
    bool GetBool(const std::string& key, bool default_value = false);

    // File storage (app-private)
    std::string GetFilesDir();
    std::string GetCacheDir();

private:
    std::string m_app_id;
    std::string m_base_path;
};
```

### Lua API

```lua
-- Key-value storage
app.storage:set("username", "john")
local name = app.storage:get("username", "anonymous")

-- File access (sandboxed)
local data = app.files:read("state.json")
app.files:write("state.json", json.encode(state))
```

---

## Implementation Plan

### Phase 1: Package Format
- [ ] Define manifest schema
- [ ] Create manifest parser/validator
- [ ] Implement .mpkg directory loader

### Phase 2: App Manager
- [ ] Install/uninstall apps
- [ ] App registry (installed apps database)
- [ ] Launch apps from package

### Phase 3: Lua Sandbox
- [ ] Restrict dangerous globals
- [ ] Inject app-specific APIs
- [ ] Per-app Lua state management

### Phase 4: Permission System
- [ ] Permission declaration in manifest
- [ ] Runtime permission checks
- [ ] Permission request UI

### Phase 5: Storage Isolation
- [ ] Per-app directories
- [ ] Key-value storage
- [ ] Quota management

---

## Security Considerations

1. **Lua Sandbox Escape**: Audit all exposed functions
2. **Path Traversal**: Validate all file paths
3. **Memory Limits**: Set Lua memory quotas
4. **CPU Limits**: Timeout long-running scripts
5. **Network Isolation**: Apps only access allowed domains

---

## Acceptance Criteria

- [ ] Apps installable from .mpkg directories
- [ ] Apps launch in isolated Lua environment
- [ ] Permission requests shown to user
- [ ] App data isolated per app
- [ ] Apps can be uninstalled cleanly
- [ ] Store app can browse and install packages
