404 lines
8.5 KiB
Markdown
404 lines
8.5 KiB
Markdown
# Packaging Apps for Neode/StartOS
|
|
|
|
This guide explains how to package containerized applications (like nostrdevs/atob) as `.s9pk` files for installation on Neode.
|
|
|
|
## What is an S9PK?
|
|
|
|
An `.s9pk` file is a package format for Neode/StartOS that contains:
|
|
- **Manifest** (metadata, dependencies, interfaces)
|
|
- **Docker Images** (your containerized app)
|
|
- **Icon** (PNG/WEBP/JPG)
|
|
- **License** (LICENSE.md)
|
|
- **Instructions** (INSTRUCTIONS.md)
|
|
- **Configuration** (optional config.yaml)
|
|
- **Actions** (optional scripts for user actions)
|
|
|
|
## Prerequisites
|
|
|
|
1. **Install StartOS SDK** (needed for packing):
|
|
```bash
|
|
# Clone the Neode repo (you already have this)
|
|
cd /Users/tx1138/Code/Neode
|
|
|
|
# Build the SDK
|
|
cd core
|
|
cargo build --release --bin startos
|
|
|
|
# The binary will be at: target/release/startos
|
|
```
|
|
|
|
2. **Docker** for building container images
|
|
|
|
## Creating an S9PK for nostrdevs/atob
|
|
|
|
### Step 1: Create Package Directory Structure
|
|
|
|
```bash
|
|
mkdir -p ~/atob-package
|
|
cd ~/atob-package
|
|
```
|
|
|
|
Create the following structure:
|
|
```
|
|
atob-package/
|
|
├── manifest.yaml # Package metadata
|
|
├── LICENSE.md # License file
|
|
├── INSTRUCTIONS.md # User instructions
|
|
├── icon.png # 512x512 icon
|
|
├── docker_images/ # Docker image archive
|
|
│ └── aarch64.tar # or x86_64.tar
|
|
└── scripts/
|
|
└── procedures/
|
|
└── main.ts # Main entry point
|
|
```
|
|
|
|
### Step 2: Create manifest.yaml
|
|
|
|
```yaml
|
|
id: atob
|
|
title: "ATOB"
|
|
version: "0.1.0"
|
|
release-notes: "Initial release"
|
|
license: MIT
|
|
wrapper-repo: "https://github.com/nostrdevs/atob"
|
|
upstream-repo: "https://github.com/nostrdevs/atob"
|
|
support-site: "https://github.com/nostrdevs/atob/issues"
|
|
marketing-site: "https://github.com/nostrdevs/atob"
|
|
donation-url: null
|
|
description:
|
|
short: "ATOB - A containerized application for Nostr"
|
|
long: |
|
|
ATOB is a containerized application designed for the Nostr ecosystem.
|
|
This package runs ATOB on your Neode server with automatic configuration.
|
|
|
|
# Assets
|
|
assets:
|
|
license: LICENSE.md
|
|
icon: icon.png
|
|
instructions: INSTRUCTIONS.md
|
|
docker-images: docker_images
|
|
|
|
# Main container
|
|
main:
|
|
type: docker
|
|
image: main
|
|
entrypoint: "docker_entrypoint.sh"
|
|
args: []
|
|
mounts:
|
|
main: /data
|
|
|
|
# Volumes
|
|
volumes:
|
|
main:
|
|
type: data
|
|
|
|
# Interfaces (exposed services)
|
|
interfaces:
|
|
main:
|
|
name: Web Interface
|
|
description: Main ATOB web interface
|
|
tor-config:
|
|
port-mapping:
|
|
80: "80"
|
|
lan-config:
|
|
443:
|
|
ssl: true
|
|
internal: 80
|
|
ui: true
|
|
protocols:
|
|
- tcp
|
|
- http
|
|
|
|
# Health checks
|
|
health-checks:
|
|
web-ui:
|
|
name: Web Interface
|
|
success-message: "ATOB is ready!"
|
|
type: docker
|
|
image: main
|
|
entrypoint: "check-web.sh"
|
|
args: []
|
|
io-format: yaml
|
|
inject: true
|
|
|
|
# Configuration (optional)
|
|
config: ~
|
|
|
|
# Properties
|
|
properties: ~
|
|
|
|
# Dependencies
|
|
dependencies: {}
|
|
|
|
# Backup configuration
|
|
backup:
|
|
create:
|
|
type: docker
|
|
image: compat
|
|
system: true
|
|
entrypoint: compat
|
|
args:
|
|
- duplicity
|
|
- create
|
|
- /mnt/backup
|
|
- /data
|
|
mounts:
|
|
BACKUP: /mnt/backup
|
|
main: /data
|
|
restore:
|
|
type: docker
|
|
image: compat
|
|
system: true
|
|
entrypoint: compat
|
|
args:
|
|
- duplicity
|
|
- restore
|
|
- /mnt/backup
|
|
- /data
|
|
mounts:
|
|
BACKUP: /mnt/backup
|
|
main: /data
|
|
|
|
# Migrations (for updates)
|
|
migrations:
|
|
from:
|
|
"*":
|
|
type: none
|
|
to:
|
|
"*":
|
|
type: none
|
|
```
|
|
|
|
### Step 3: Create LICENSE.md
|
|
|
|
Copy your project's license or create a simple one:
|
|
|
|
```markdown
|
|
# MIT License
|
|
|
|
Copyright (c) 2025 Nostr Devs
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction...
|
|
```
|
|
|
|
### Step 4: Create INSTRUCTIONS.md
|
|
|
|
```markdown
|
|
# ATOB Instructions
|
|
|
|
## Getting Started
|
|
|
|
1. After installation, ATOB will be available at the interface URL
|
|
2. Access it through the Neode dashboard
|
|
3. Configuration is automatic
|
|
|
|
## Usage
|
|
|
|
[Add specific instructions for your app]
|
|
|
|
## Support
|
|
|
|
For issues, visit: https://github.com/nostrdevs/atob/issues
|
|
```
|
|
|
|
### Step 5: Add an Icon
|
|
|
|
Create or download a 512x512 PNG icon and save it as `icon.png`
|
|
|
|
### Step 6: Export Docker Image
|
|
|
|
```bash
|
|
# Build your Docker image
|
|
cd /path/to/atob
|
|
docker build -t atob:latest .
|
|
|
|
# Save the image
|
|
mkdir -p ~/atob-package/docker_images
|
|
docker save atob:latest -o ~/atob-package/docker_images/$(uname -m).tar
|
|
|
|
# The filename should match your architecture:
|
|
# - x86_64.tar for Intel/AMD
|
|
# - aarch64.tar for ARM64/Apple Silicon
|
|
```
|
|
|
|
### Step 7: Create scripts/procedures/main.ts
|
|
|
|
This is the entry point for your service:
|
|
|
|
```typescript
|
|
import { types as T, matches, YAML } from "../deps.ts";
|
|
|
|
// This is the main entry point for your service
|
|
export const main: T.ExpectedExports.main = async (effects: T.Effects) => {
|
|
return await effects.createContainer({
|
|
image: "main",
|
|
entrypoint: ["/bin/sh"],
|
|
mounts: {
|
|
main: "/data",
|
|
},
|
|
});
|
|
};
|
|
|
|
// Properties that will be displayed in the UI
|
|
export const properties: T.ExpectedExports.properties = async (
|
|
effects: T.Effects
|
|
) => {
|
|
return {
|
|
version: "0.1.0",
|
|
"Automatic TOR Address": {
|
|
type: "string",
|
|
value: effects.interfaces.main.torAddress,
|
|
qr: true,
|
|
copyable: true,
|
|
masked: false,
|
|
},
|
|
};
|
|
};
|
|
|
|
// Health check
|
|
export const health: T.ExpectedExports.health = async (effects: T.Effects) => {
|
|
return await effects.health.checkWebUrl("http://main.embassy:80");
|
|
};
|
|
```
|
|
|
|
### Step 8: Build the S9PK
|
|
|
|
```bash
|
|
# Navigate to your package directory
|
|
cd ~/atob-package
|
|
|
|
# Use the StartOS CLI to pack it
|
|
/Users/tx1138/Code/Neode/core/target/release/startos pack
|
|
|
|
# This will create: atob.s9pk
|
|
```
|
|
|
|
### Step 9: Install on Neode
|
|
|
|
**Option A: Via CLI (Direct)**
|
|
```bash
|
|
# Copy the .s9pk to your Neode server
|
|
scp atob.s9pk user@neode-server:/tmp/
|
|
|
|
# SSH into the server
|
|
ssh user@neode-server
|
|
|
|
# Install using CLI
|
|
startos package.sideload /tmp/atob.s9pk
|
|
```
|
|
|
|
**Option B: Via UI (Once Marketplace is Connected)**
|
|
1. Navigate to Marketplace in Neode UI
|
|
2. Click "Sideload Package"
|
|
3. Upload `atob.s9pk`
|
|
4. Wait for installation to complete
|
|
|
|
## Testing Your Package
|
|
|
|
### Validate Before Installing
|
|
```bash
|
|
# Inspect the package without installing
|
|
/Users/tx1138/Code/Neode/core/target/release/startos inspect atob.s9pk
|
|
```
|
|
|
|
### Development Workflow
|
|
|
|
1. **Make changes** to your manifest or scripts
|
|
2. **Rebuild** the s9pk: `startos pack`
|
|
3. **Uninstall** old version: `startos package.uninstall atob`
|
|
4. **Install** new version: `startos package.sideload atob.s9pk`
|
|
|
|
## Advanced Features
|
|
|
|
### Adding Configuration Options
|
|
|
|
Add to `manifest.yaml`:
|
|
|
|
```yaml
|
|
config:
|
|
get:
|
|
type: script
|
|
set:
|
|
type: script
|
|
|
|
# Then create scripts/procedures/getConfig.ts and setConfig.ts
|
|
```
|
|
|
|
### Adding User Actions
|
|
|
|
```yaml
|
|
actions:
|
|
restart-service:
|
|
name: "Restart Service"
|
|
description: "Manually restart the ATOB service"
|
|
warning: "This will temporarily interrupt service"
|
|
allowed-statuses:
|
|
- running
|
|
implementation:
|
|
type: docker
|
|
image: main
|
|
entrypoint: "restart.sh"
|
|
```
|
|
|
|
### Multi-Architecture Support
|
|
|
|
Build for multiple architectures:
|
|
|
|
```bash
|
|
# Build for x86_64
|
|
docker buildx build --platform linux/amd64 -t atob:amd64 .
|
|
docker save atob:amd64 -o docker_images/x86_64.tar
|
|
|
|
# Build for ARM64
|
|
docker buildx build --platform linux/arm64 -t atob:arm64 .
|
|
docker save atob:arm64 -o docker_images/aarch64.tar
|
|
```
|
|
|
|
## Resources
|
|
|
|
- **StartOS Package Manifest Schema**: [Official Docs](https://docs.start9.com)
|
|
- **Example Packages**: `/Users/tx1138/Code/Neode/core/startos/test/`
|
|
- **SDK Reference**: Built binaries in `core/target/release/`
|
|
|
|
## Troubleshooting
|
|
|
|
### Package Won't Install
|
|
- Check manifest syntax: `yamllint manifest.yaml`
|
|
- Verify docker image exists: `tar -tzf docker_images/aarch64.tar | head`
|
|
- Check logs on server: `journalctl -u startos -f`
|
|
|
|
### Service Won't Start
|
|
- Check container logs: `docker logs $(docker ps -a | grep atob | awk '{print $1}')`
|
|
- Verify entrypoint script exists and is executable
|
|
- Check volume mounts in manifest
|
|
|
|
### Interface Not Accessible
|
|
- Verify port mappings in `interfaces` section
|
|
- Check that your container is listening on the correct port
|
|
- Wait for TOR address generation (can take 2-3 minutes)
|
|
|
|
## Quick Reference
|
|
|
|
```bash
|
|
# Pack a package
|
|
startos pack
|
|
|
|
# Inspect a package
|
|
startos inspect atob.s9pk
|
|
|
|
# Install (CLI)
|
|
startos package.sideload atob.s9pk
|
|
|
|
# List installed packages
|
|
startos package.list
|
|
|
|
# Uninstall
|
|
startos package.uninstall atob
|
|
|
|
# Check package status
|
|
startos package.properties atob
|
|
```
|
|
|