# Milestone 1: App Package Format

**Status**: Decided
**Goal**: Define how apps are bundled, signed, and validated.

## Decision

**Signed ZIP (Option C)** with JAR/APK-style signing using Ed25519:

```
Format:       ZIP archive with .mosis extension
Signing:      Ed25519 (crypto/ed25519 stdlib)
Manifest:     META-INF/MANIFEST.MF with SHA-256 hashes
Validation:   Go package (mosis-portal/pkg/package)
```

### Rationale

1. **Standard tooling** - ZIP format works with all archive tools
2. **Proven approach** - JAR/APK signing is battle-tested
3. **Ed25519** - Fast, secure, small signatures (64 bytes)
4. **Go stdlib** - crypto/ed25519 and archive/zip in standard library
5. **Easy inspection** - Developers can unzip and view contents

### Package Structure

```
com.developer.appname-1.0.0.mosis  (ZIP archive)
├── manifest.json           # App metadata (JSON)
├── META-INF/
│   ├── MANIFEST.MF         # SHA-256 hashes of all files
│   └── CERT.SIG            # Ed25519 signature of MANIFEST.MF
├── icons/
│   ├── icon-32.png
│   ├── icon-64.png
│   └── icon-128.png
└── assets/
    ├── main.rml            # Entry point
    ├── styles/
    │   └── theme.rcss
    └── scripts/
        └── app.lua
```

---

## Overview

The app package format is the foundation of the entire ecosystem. Every tool (CLI, portal, device) needs to understand this format.

---

## Questions to Answer

1. What files comprise an app package?
2. How is the manifest structured?
3. How are apps signed for integrity?
4. How are updates handled (full vs delta)?
5. What metadata is required (name, version, permissions, icons)?

---

## Package Format Options

### Option A: ZIP Archive

```
myapp.zip
├── manifest.json
├── icon.png
└── assets/
    ├── main.rml
    ├── styles.rcss
    └── app.lua
```

| Pros | Cons |
|------|------|
| Simple, standard tooling | No built-in signing |
| Easy to inspect | No metadata in filename |
| Wide compatibility | Need separate signature file |

### Option B: Custom Format (.mosis)

```
myapp.mosis (binary format)
┌─────────────────────────┐
│ Magic bytes (4)         │
│ Version (2)             │
│ Manifest length (4)     │
│ Manifest JSON           │
│ Signature (256)         │
│ Compressed payload      │
└─────────────────────────┘
```

| Pros | Cons |
|------|------|
| Signature built-in | Custom tooling needed |
| Efficient metadata access | Harder to inspect |
| Single file | More complex implementation |

### Option C: Signed ZIP (Recommended)

```
myapp.mosis/  (actually a ZIP)
├── manifest.json
├── META-INF/
│   ├── MANIFEST.MF      # File hashes
│   └── SIGNATURE.SF     # Signed manifest
├── icon.png
└── assets/
    └── ...
```

| Pros | Cons |
|------|------|
| Standard ZIP tooling | Slightly more complex |
| JAR/APK-style signing | |
| Easy inspection | |
| Proven approach | |

---

## Proposed Structure

```
com.developer.appname-1.0.0.mosis
├── manifest.json           # App metadata
├── META-INF/
│   ├── CERT.PEM            # Developer certificate
│   ├── CERT.SIG            # Signature of MANIFEST.MF
│   └── MANIFEST.MF         # SHA-256 hashes of all files
├── icons/
│   ├── icon-32.png
│   ├── icon-64.png
│   └── icon-128.png
├── assets/
│   ├── main.rml            # Entry point
│   ├── screens/
│   │   ├── home.rml
│   │   └── settings.rml
│   ├── styles/
│   │   └── theme.rcss
│   └── scripts/
│       ├── main.lua
│       └── utils.lua
└── locales/                # Optional i18n
    ├── en.json
    └── es.json
```

---

## Manifest Schema

### Required Fields

```json
{
  "$schema": "https://mosis.dev/schemas/manifest-v1.json",
  "id": "com.developer.appname",
  "name": "My App",
  "version": "1.0.0",
  "version_code": 1,
  "entry": "assets/main.rml",
  "min_mosis_version": "1.0.0"
}
```

### Full Schema

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

  "id": "com.developer.appname",
  "name": "My App",
  "version": "1.0.0",
  "version_code": 1,

  "description": "A short description of the app",
  "entry": "assets/main.rml",

  "author": {
    "name": "Developer Name",
    "email": "dev@example.com",
    "url": "https://developer.com"
  },

  "permissions": [
    "storage",
    "network",
    "camera"
  ],

  "icons": {
    "32": "icons/icon-32.png",
    "64": "icons/icon-64.png",
    "128": "icons/icon-128.png"
  },

  "min_mosis_version": "1.0.0",
  "target_mosis_version": "1.2.0",

  "category": "utilities",
  "tags": ["productivity", "tools"],

  "orientation": "portrait",
  "background_color": "#FFFFFF",

  "locales": ["en", "es", "fr"],
  "default_locale": "en"
}
```

### Field Definitions

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | Unique package identifier (reverse domain) |
| `name` | string | Yes | Display name (max 30 chars) |
| `version` | string | Yes | Semantic version (X.Y.Z) |
| `version_code` | integer | Yes | Incremental build number |
| `entry` | string | Yes | Path to entry RML file |
| `min_mosis_version` | string | Yes | Minimum Mosis version required |
| `description` | string | No | Short description (max 80 chars) |
| `author` | object | No | Author information |
| `permissions` | array | No | Required permissions |
| `icons` | object | No | Icon paths by size |
| `category` | string | No | App store category |
| `tags` | array | No | Searchable tags |
| `orientation` | string | No | portrait, landscape, any |
| `background_color` | string | No | Hex color for loading |
| `locales` | array | No | Supported locales |

---

## Signing Mechanism

### Algorithm

- **Key type**: Ed25519 (fast, secure, small signatures)
- **Hash**: SHA-256 for file manifests
- **Format**: PEM for keys, base64 for signatures

### MANIFEST.MF Format

```
Manifest-Version: 1.0
Created-By: mosis-cli 1.0.0

Name: manifest.json
SHA-256-Digest: base64encodedHash==

Name: assets/main.rml
SHA-256-Digest: base64encodedHash==

Name: assets/scripts/main.lua
SHA-256-Digest: base64encodedHash==
```

### Signing Flow

```
1. Generate MANIFEST.MF with SHA-256 of each file
2. Sign MANIFEST.MF with developer's Ed25519 private key
3. Store signature in META-INF/CERT.SIG
4. Include developer's public key in META-INF/CERT.PEM
```

### Verification Flow

```
1. Extract META-INF/MANIFEST.MF
2. Verify signature using CERT.PEM
3. Verify CERT.PEM is registered with developer account
4. Verify each file hash matches MANIFEST.MF
```

---

## Size Limits

| Limit | Value | Rationale |
|-------|-------|-----------|
| Max package size | 50 MB | Reasonable for mobile |
| Max individual file | 10 MB | Prevent abuse |
| Max files count | 1000 | Prevent zip bombs |
| Max path length | 256 chars | Filesystem compat |
| Max manifest size | 64 KB | Prevent abuse |

---

## Validation Rules

### Manifest Validation

- [ ] Valid JSON
- [ ] All required fields present
- [ ] `id` matches reverse domain pattern: `^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$`
- [ ] `version` matches semver pattern
- [ ] `version_code` is positive integer
- [ ] `entry` file exists in package
- [ ] All `permissions` are valid permission names
- [ ] All `icons` paths exist and are valid images

### Package Validation

- [ ] Valid ZIP format
- [ ] manifest.json at root
- [ ] No path traversal (../)
- [ ] No absolute paths
- [ ] No symlinks
- [ ] Under size limits
- [ ] No duplicate files
- [ ] Valid file extensions only

### Signature Validation

- [ ] MANIFEST.MF present
- [ ] CERT.SIG present
- [ ] CERT.PEM present
- [ ] Signature valid for MANIFEST.MF
- [ ] All file hashes match
- [ ] Certificate registered with developer account (for store)

---

## File Extension Rules

### Allowed Extensions

| Category | Extensions |
|----------|------------|
| UI | .rml |
| Styles | .rcss |
| Scripts | .lua |
| Images | .png, .jpg, .jpeg, .tga, .webp |
| Fonts | .ttf, .otf |
| Data | .json |
| Localization | .json (in locales/) |
| Audio | .ogg, .wav, .mp3 |

### Forbidden

- Executables: .exe, .dll, .so, .dylib
- Scripts: .sh, .bat, .ps1, .py, .js
- Archives: .zip, .tar, .gz (nested)

---

## Update Handling

### Full Update

- Download complete new package
- Verify signature
- Atomic replacement of app directory
- Preserve user data in separate location

### Delta Updates (Future)

- bsdiff-style patches
- Reduces bandwidth for minor updates
- More complex implementation
- Consider for v2

---

## Deliverables

- [x] Package format decided (Signed ZIP with .mosis extension)
- [x] Signing algorithm decided (Ed25519)
- [ ] JSON Schema for manifest validation
- [ ] Go package: `pkg/package/manifest.go` (parsing/validation)
- [ ] Go package: `pkg/package/validator.go` (package validation)
- [ ] Go package: `pkg/package/signer.go` (Ed25519 signing/verification)
- [ ] Integration with mosis-cli `build` and `sign` commands

---

## Test Cases

| Test | Description |
|------|-------------|
| ValidPackage | Accept well-formed package |
| InvalidManifest | Reject malformed JSON |
| MissingRequired | Reject missing required fields |
| BadSignature | Reject invalid signature |
| TamperedFile | Reject if file hash mismatch |
| PathTraversal | Reject ../ in paths |
| OversizePackage | Reject over size limit |
| BadExtension | Reject forbidden file types |
| DuplicateFiles | Reject duplicate entries |

---

## Open Questions

1. ~~Should we support multiple entry points (e.g., widget vs full app)?~~ → Single entry point for v1
2. ~~Should icons be required or have defaults?~~ → Required (32, 64, 128 sizes)
3. ~~Delta updates in v1 or defer to v2?~~ → Defer to v2 (full updates only)
4. ~~Support for app bundles (multiple apps in one package)?~~ → No, one app per package

---

## References

- [Android APK format](https://developer.android.com/guide/components/fundamentals)
- [JAR signing](https://docs.oracle.com/javase/tutorial/deployment/jar/signing.html)
- [Ed25519 specification](https://ed25519.cr.yp.to/)
