Package Structure¶
Understanding the internal structure of PSPF packages and how they organize content into executable bundles.
Overview¶
A PSPF package is a self-contained executable file that combines:
- Native Launcher: Platform-specific executable header
- Index Block: Fixed-size metadata and signature block
- Slot Table: Directory of content slots
- Metadata: Compressed JSON manifest
- Content Slots: Application code, data, and resources
- Magic Trailer: Package validation footer
Binary Layout¶
Overall Structure¶
┌─────────────────────────┐ Offset 0
│ Native Launcher │ ~2-3 MB
│ (Go/Rust binary) │
├─────────────────────────┤ launcher_size
│ Index Block │ 8192 bytes
│ (metadata, crypto) │
├─────────────────────────┤
│ Slot Table │ Variable
│ (64 bytes × slots) │
├─────────────────────────┤
│ Metadata (gzip) │ Variable
├─────────────────────────┤
│ Slot 0 Data │
├─────────────────────────┤
│ Slot 1 Data │
├─────────────────────────┤
│ ... │
├─────────────────────────┤
│ Slot N Data │
├─────────────────────────┤
│ Magic Trailer │ 16 bytes
│ "📦🪄"+CRC32 │
└─────────────────────────┘ EOF
Size Breakdown¶
Typical package sizes:
| Component | Size | Percentage |
|---|---|---|
| Launcher | 2.5 MB | 5% |
| Index Block | 8 KB | <1% |
| Metadata | 10 KB | <1% |
| Python venv | 25 MB | 50% |
| Application | 20 MB | 40% |
| Resources | 2.5 MB | 5% |
| Total | 50 MB | 100% |
Component Details¶
1. Native Launcher¶
The executable header that starts package execution:
// Go launcher structure
type Launcher struct {
Binary []byte // Platform-specific executable
Platform string // linux_amd64, darwin_arm64, etc.
Version string // Launcher version
Features []string // Supported capabilities
}
Responsibilities: - Find and read index block - Verify package signature - Extract slots to work environment - Execute Python application - Clean up temporary files
2. Index Block¶
Fixed 8192-byte structure containing:
# Index block layout (8192 bytes)
class IndexBlock:
format_version: uint32 # 0x20250000
metadata_offset: uint64 # Offset to metadata
metadata_size: uint64 # Size of metadata
slot_table_offset: uint64 # Offset to slot table
public_key: bytes[32] # Ed25519 public key
signature: bytes[64] # Package signature
launcher_size: uint64 # Size of launcher
slot_count: uint32 # Number of slots
index_checksum: uint32 # CRC32 of index
# ... padding to 8192 bytes
3. Slot Table¶
Directory of content slots:
# Each slot descriptor (64 bytes)
class SlotDescriptor:
# Core fields (56 bytes - 7 × uint64)
id: uint64 # Unique slot identifier
name_hash: uint64 # SHA-256 of slot name (first 8 bytes)
offset: uint64 # Byte offset in package
size: uint64 # Size as stored (after operations)
original_size: uint64 # Size before operations
operations: uint64 # Packed operation chain (up to 8 ops)
checksum: uint64 # SHA-256 (first 8 bytes)
# Metadata fields (8 bytes - 8 × uint8)
purpose: uint8 # 0=data, 1=code, 2=config, 3=media
lifecycle: uint8 # When to extract/use
priority: uint8 # Cache priority (0-255)
platform: uint8 # Platform requirements
reserved1: uint8 # Reserved
reserved2: uint8 # Reserved
permissions: uint8 # Unix permissions (low byte)
permissions_high: uint8 # Unix permissions (high byte)
4. Metadata¶
Compressed JSON containing:
{
"package": {
"name": "myapp",
"version": "1.0.0",
"description": "Application description"
},
"execution": {
"entry_point": "app:main",
"python_version": "3.11",
"environment": {}
},
"slots": [
{
"id": "python-venv",
"purpose": "python-environment",
"lifecycle": "persistent",
"extract_to": "venv",
"operations": "tar.gz",
"size": 25000000,
"checksum": "sha256:abc123..."
},
{
"id": "app-code",
"purpose": "application-code",
"lifecycle": "persistent",
"extract_to": "app",
"operations": "tar",
"size": 20000000
}
],
"build": {
"timestamp": 1704619815,
"builder": "flavor-go-builder",
"seed": "deterministic-seed"
}
}
Slot Organization¶
Standard Slot Layout¶
Most packages follow this pattern:
slots/
├── 0: python-venv # Python virtual environment
├── 1: application # Main application code
├── 2: configuration # Config files
├── 3: static-resources # Images, CSS, JS
├── 4: data-files # Databases, models
└── 5: scripts # Helper scripts
Slot Purposes¶
| Purpose | Description | Typical Content |
|---|---|---|
python-environment |
Python venv | site-packages, pip |
application-code |
Main code | Python modules |
configuration |
Settings | JSON, YAML, TOML |
static-resources |
Assets | Images, fonts |
native-binary |
Compiled code | .so, .dll files |
data-files |
Application data | SQLite, models |
documentation |
Docs | README, help |
scripts |
Utilities | Shell, Python |
Slot Lifecycles¶
Control when slots are extracted:
SLOT_LIFECYCLES = {
"persistent": "Extract once, keep forever",
"volatile": "Extract on startup, clean after init",
"temporary": "Extract on startup, clean on exit",
"cached": "Extract on demand, clean by policy",
"init-only": "Extract first run only",
"lazy": "Extract when accessed",
"eager": "Extract immediately"
}
Building Packages¶
Source Structure¶
Typical project layout:
myproject/
├── pyproject.toml # Manifest file
├── src/
│ └── myapp/ # Application code
│ ├── __init__.py
│ └── main.py
├── config/
│ └── settings.yaml # Configuration
├── static/ # Static resources
│ ├── css/
│ └── images/
└── requirements.txt # Dependencies
Manifest Configuration¶
[project]
name = "myapp"
version = "1.0.0"
dependencies = [
"flask>=2.0",
"sqlalchemy>=2.0"
]
[tool.flavor]
entry_point = "myapp:main"
python_version = "3.11"
[[tool.flavor.slots]]
id = "config"
source = "config/"
purpose = "configuration"
lifecycle = "persistent"
[[tool.flavor.slots]]
id = "static"
source = "static/"
purpose = "static-resources"
lifecycle = "cached"
operations = "tar.gz"
Build Process¶
The packaging process transforms your Python application into a self-contained executable:
flowchart TD
Start([flavor pack]) --> ParseManifest[1. Parse pyproject.toml]
ParseManifest --> SelectHelpers{2. Select Helpers}
SelectHelpers --> LauncherChoice[Choose Launcher]
SelectHelpers --> BuilderChoice[Choose Builder]
LauncherChoice --> BuildEnv[3. Build Python Environment]
BuilderChoice --> BuildEnv
BuildEnv --> InstallDeps[Install dependencies with UV]
InstallDeps --> PrepareSlots[4. Prepare Content Slots]
PrepareSlots --> Slot0[Slot 0: Python venv]
PrepareSlots --> Slot1[Slot 1: App code]
PrepareSlots --> SlotN[Slot N: Resources]
Slot0 --> CompressSlots[5. Compress & Hash]
Slot1 --> CompressSlots
SlotN --> CompressSlots
CompressSlots --> GenMetadata[6. Generate JSON Metadata]
GenMetadata --> SignPkg{7. Sign Package?}
SignPkg -->|With Keys| GenKeys[Use provided keys]
SignPkg -->|Auto| AutoKeys[Generate ephemeral keys]
GenKeys --> CreateSig[Create Ed25519 signature]
AutoKeys --> CreateSig
CreateSig --> Assemble[8. Assemble Binary]
Assemble --> AddLauncher[Prepend launcher binary]
AddLauncher --> AddIndex[Add index block]
AddIndex --> AddMetadata[Add compressed metadata]
AddMetadata --> AddSlots[Append slot data]
AddSlots --> AddTrailer[9. Add magic trailer 📦🪄]
AddTrailer --> Verify{Verify?}
Verify -->|Yes| RunVerify[flavor verify]
Verify -->|No| Done
RunVerify --> Done([✅ Package complete])
style Start fill:#e1f5fe
style Done fill:#c8e6c9
style BuildEnv fill:#fff9c4
style SignPkg fill:#f3e5f5
style Assemble fill:#ffe0b2
Step Details:
- Parse Manifest: Read and validate
pyproject.tomlconfiguration - Select Helpers: Choose appropriate launcher/builder for target platform
- Build Environment: Create isolated Python virtual environment with UV
- Prepare Slots: Organize content into numbered slots (0=runtime, 1=app, 2+=resources)
- Compress & Hash: Apply operations (tar.gz, etc.) and compute SHA-256 checksums
- Generate Metadata: Create JSON manifest with package info and slot descriptors
- Sign Package: Generate Ed25519 signature for integrity verification
- Assemble Binary: Combine launcher + index + metadata + slots into single file
- Add Trailer: Append magic bytes (
📦🪄) and CRC32 for format identification
Execution Flow¶
Package Startup¶
When a user runs a .psp package, the launcher orchestrates extraction and execution:
flowchart TD
Start([User: ./myapp.psp]) --> OSExec[OS loads executable]
OSExec --> LauncherInit[Launcher initializes]
LauncherInit --> FindIndex[Seek to EOF - 16 bytes]
FindIndex --> ReadMagic{Magic bytes<br/>📦🪄 present?}
ReadMagic -->|No| Error1[❌ Invalid package]
ReadMagic -->|Yes| ReadIndexBlock[Read 8KB index block]
ReadIndexBlock --> VerifySig{Verify Ed25519<br/>signature?}
VerifySig -->|Invalid| Error2[❌ Signature failed]
VerifySig -->|Valid| ReadMeta[Decompress metadata JSON]
ReadMeta --> CalcCacheID[Calculate package ID<br/>SHA-256 of metadata]
CalcCacheID --> CheckCache{Work environment<br/>exists in cache?}
CheckCache -->|Yes| ValidateCache{Cache<br/>valid?}
CheckCache -->|No| CreateWorkDir[Create cache directory]
ValidateCache -->|Invalid| CreateWorkDir
ValidateCache -->|Valid| SetEnv[Set FLAVOR_WORKENV]
CreateWorkDir --> ExtractSlots[Extract slots by lifecycle]
ExtractSlots --> ExtractPersistent[Extract persistent slots]
ExtractPersistent --> ExtractCached[Extract cached slots]
ExtractCached --> RegisterLazy[Register lazy slots]
RegisterLazy --> SetEnv
SetEnv --> FindPython[Locate Python in slot 0]
FindPython --> ExecApp[Execute entry point]
ExecApp --> AppRuns[Application running]
AppRuns --> AppExit{App<br/>exits}
AppExit --> CleanVolatile[Clean volatile slots]
CleanVolatile --> Done([✅ Complete])
Error1 --> Failed([❌ Failed])
Error2 --> Failed
style Start fill:#e1f5fe
style Done fill:#c8e6c9
style Failed fill:#ffcdd2
style VerifySig fill:#fff3e0
style CheckCache fill:#e8eaf6
style AppRuns fill:#f3e5f5
Startup Phases:
- Validation (0.1s): Verify magic bytes, index block, and Ed25519 signature
- Cache Check (0.01s): Calculate package ID and check if work environment exists
- Extraction (2-5s first run, 0s cached): Extract slots to
~/.cache/flavor/workenv/ - Execution (<0.1s): Set environment variables and launch Python interpreter
Performance: - First run: 2-5 seconds (extraction time) - Subsequent runs: <1 second (cached workenv) - Cache hit: Near-instant startup
Extraction Strategy¶
Slots are extracted based on lifecycle:
def extract_package(metadata, work_dir):
for slot in metadata["slots"]:
target = work_dir / slot["extract_to"]
if slot["lifecycle"] == "persistent":
if not target.exists():
extract_slot(slot, target)
elif slot["lifecycle"] == "volatile":
extract_slot(slot, target)
schedule_cleanup(target, "after_init")
elif slot["lifecycle"] == "lazy":
# Extract on first access
register_lazy_extractor(slot, target)
Optimization Strategies¶
Size Optimization¶
- Compression: Use appropriate operations
tar.gzfor text-heavy contenttarfor already-compressed data-
rawfor small files -
Deduplication: Share common libraries
-
Lazy Loading: Defer optional content
Performance Optimization¶
- Parallel Extraction: Extract slots concurrently
- Memory Mapping: Use mmap for large files
- Caching: Reuse extracted environments
- Streaming: Process without full extraction
Security Features¶
Package Integrity¶
- Ed25519 Signatures: Cryptographic authenticity
- SHA-256 Checksums: Content verification
- CRC32 Validation: Quick corruption detection
Execution Safety¶
- Isolated Environments: Separate work directories
- Permission Control: Restricted file access
- Path Sanitization: Prevent directory traversal
Debugging Packages¶
Inspection Tools¶
# View package structure
flavor inspect package.psp
# Extract specific slot
flavor extract package.psp --slot app-code
# Verify integrity
flavor verify package.psp --deep
# Debug execution
FLAVOR_LOG_LEVEL=debug ./package.psp
Common Issues¶
| Issue | Cause | Solution |
|---|---|---|
| Large package size | Uncompressed slots | Use compression |
| Slow startup | Re-extraction | Enable caching |
| Missing files | Wrong lifecycle | Check slot config |
| Signature failure | Corruption | Rebuild package |
Best Practices¶
- Organize Slots Logically: Group related content
- Choose Appropriate Lifecycles: Match usage patterns
- Compress Efficiently: Balance size vs speed
- Version Carefully: Use semantic versioning
- Test Extraction: Verify all platforms
- Document Structure: Include README in package
- Monitor Performance: Profile extraction times
Related Documentation¶
- Binary Layout - Technical format details
- Slots - Slot system specification
- Metadata - Metadata structure
- Building Packages - Build guide