373 lines
8.7 KiB
Markdown
373 lines
8.7 KiB
Markdown
# Milestone 1: App Package Format
|
|
|
|
**Status**: Planning
|
|
**Goal**: Define how apps are bundled, signed, and validated.
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
- [ ] JSON Schema for manifest validation
|
|
- [ ] Package format specification document
|
|
- [ ] Reference implementation: package creator (Go/Rust)
|
|
- [ ] Reference implementation: package validator
|
|
- [ ] Reference implementation: signature tools
|
|
- [ ] Integration with mosis-cli
|
|
|
|
---
|
|
|
|
## 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)?
|
|
2. Should icons be required or have defaults?
|
|
3. Delta updates in v1 or defer to v2?
|
|
4. Support for app bundles (multiple apps in one 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/)
|