10 KiB
10 KiB
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
- Standard tooling - ZIP format works with all archive tools
- Proven approach - JAR/APK signing is battle-tested
- Ed25519 - Fast, secure, small signatures (64 bytes)
- Go stdlib - crypto/ed25519 and archive/zip in standard library
- 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
- What files comprise an app package?
- How is the manifest structured?
- How are apps signed for integrity?
- How are updates handled (full vs delta)?
- 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
{
"$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
{
"$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
idmatches reverse domain pattern:^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$versionmatches semver patternversion_codeis positive integerentryfile exists in package- All
permissionsare valid permission names - All
iconspaths 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
- Package format decided (Signed ZIP with .mosis extension)
- 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
buildandsigncommands
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
Should we support multiple entry points (e.g., widget vs full app)?→ Single entry point for v1Should icons be required or have defaults?→ Required (32, 64, 128 sizes)Delta updates in v1 or defer to v2?→ Defer to v2 (full updates only)Support for app bundles (multiple apps in one package)?→ No, one app per package