Initial BitNet s9pk package with SSH access
This commit is contained in:
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# BitNet s9pk - Extended with SSH access
|
||||||
|
FROM ghcr.io/kth8/bitnet:latest
|
||||||
|
|
||||||
|
# Install OpenSSH server and dependencies
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y openssh-server && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create SSH directory and configure
|
||||||
|
RUN mkdir -p /var/run/sshd /root/.ssh && \
|
||||||
|
chmod 700 /root/.ssh
|
||||||
|
|
||||||
|
# Configure SSH
|
||||||
|
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && \
|
||||||
|
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
|
||||||
|
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# Create entrypoint script
|
||||||
|
COPY docker_entrypoint.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/docker_entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 22
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/docker_entrypoint.sh"]
|
||||||
|
CMD ["/usr/sbin/sshd", "-D"]
|
||||||
29
Makefile
Normal file
29
Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# BitNet s9pk Makefile
|
||||||
|
PKG_ID := bitnet
|
||||||
|
PKG_VERSION := 1.0.0
|
||||||
|
TS_FILES := $(shell find . -name '*.ts' 2>/dev/null)
|
||||||
|
|
||||||
|
.PHONY: all clean install
|
||||||
|
|
||||||
|
# Default image
|
||||||
|
all: $(PKG_ID).s9pk
|
||||||
|
|
||||||
|
install: $(PKG_ID).s9pk
|
||||||
|
start-cli package install $(PKG_ID).s9pk
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PKG_ID).s9pk
|
||||||
|
rm -f image.tar
|
||||||
|
|
||||||
|
# Build Docker image using remote Docker on MacBook
|
||||||
|
image.tar: Dockerfile docker_entrypoint.sh
|
||||||
|
@echo "Building Docker image on remote Docker (macbook)..."
|
||||||
|
docker -H ssh://macbook build --platform linux/amd64 --tag start9/$(PKG_ID)/main:$(PKG_VERSION) -o type=docker,dest=image.tar .
|
||||||
|
|
||||||
|
# Pack the s9pk
|
||||||
|
$(PKG_ID).s9pk: manifest.yaml image.tar instructions.md scripts/*.sh
|
||||||
|
@echo "Packing s9pk..."
|
||||||
|
start-cli s9pk pack \
|
||||||
|
--arch aarch64 \
|
||||||
|
--arch x86_64 \
|
||||||
|
.
|
||||||
105
README.md
Normal file
105
README.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# BitNet s9pk Package
|
||||||
|
|
||||||
|
StartOS package for BitNet LLM with SSH access.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
bitnet-s9pk/
|
||||||
|
├── Dockerfile # Extended BitNet image with SSH
|
||||||
|
├── docker_entrypoint.sh # Entrypoint script for SSH setup
|
||||||
|
├── manifest.yaml # StartOS package manifest
|
||||||
|
├── instructions.md # User-facing instructions
|
||||||
|
├── Makefile # Build automation
|
||||||
|
├── scripts/
|
||||||
|
│ ├── config_get.sh # Config schema
|
||||||
|
│ ├── config_set.sh # Config application
|
||||||
|
│ ├── properties.sh # Package properties
|
||||||
|
│ └── health_check.sh # Health check script
|
||||||
|
└── assets/
|
||||||
|
└── icon.png # Package icon (TODO)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker access (local or remote via SSH to MacBook)
|
||||||
|
- `start-cli` installed and authenticated
|
||||||
|
- Developer key at `/data/.startos/developer.key.pem`
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Option 1: Using Makefile (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /data/.openclaw/workspace/bitnet-s9pk
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. Build the Docker image on remote Docker (MacBook)
|
||||||
|
2. Export it as `image.tar`
|
||||||
|
3. Pack everything into `bitnet.s9pk`
|
||||||
|
|
||||||
|
### Option 2: Manual Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build image on MacBook
|
||||||
|
docker -H ssh://macbook build --platform linux/amd64 \
|
||||||
|
--tag start9/bitnet/main:1.0.0 \
|
||||||
|
-o type=docker,dest=image.tar .
|
||||||
|
|
||||||
|
# Pack the s9pk
|
||||||
|
start-cli s9pk pack --arch aarch64 --arch x86_64 .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install on your StartOS server
|
||||||
|
start-cli package install bitnet.s9pk
|
||||||
|
|
||||||
|
# Or use make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration After Install
|
||||||
|
|
||||||
|
1. Go to BitNet service in StartOS UI
|
||||||
|
2. Navigate to **Config** tab
|
||||||
|
3. Paste your SSH public key
|
||||||
|
4. Save and restart the service
|
||||||
|
|
||||||
|
## Connecting via SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via Tor (get address from Interfaces tab)
|
||||||
|
ssh -o ProxyCommand="nc -x localhost:9050 %h %p" root@your-bitnet-address.onion
|
||||||
|
|
||||||
|
# Via LAN
|
||||||
|
ssh root@your-server-lan-ip -p <port>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using BitNet
|
||||||
|
|
||||||
|
Once connected:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run inference
|
||||||
|
python3 /BitNet/run_inference.py -m ggml-model-i2_s.gguf -p "Hello, world!"
|
||||||
|
|
||||||
|
# See help
|
||||||
|
python3 /BitNet/run_inference.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] Add proper icon.png (512x512)
|
||||||
|
- [ ] Test on actual StartOS hardware
|
||||||
|
- [ ] Add model download action
|
||||||
|
- [ ] Volume mount for custom models
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- BitNet: https://github.com/microsoft/BitNet
|
||||||
|
- Container: https://github.com/kth8/bitnet
|
||||||
|
- StartOS Docs: https://docs.start9.com/latest/developer-docs/
|
||||||
17
docker_entrypoint.sh
Executable file
17
docker_entrypoint.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Setup SSH authorized keys from environment variable
|
||||||
|
if [ -n "$SSH_AUTHORIZED_KEYS" ]; then
|
||||||
|
echo "$SSH_AUTHORIZED_KEYS" > /root/.ssh/authorized_keys
|
||||||
|
chmod 600 /root/.ssh/authorized_keys
|
||||||
|
echo "SSH authorized keys configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate host keys if they don't exist
|
||||||
|
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
|
||||||
|
ssh-keygen -A
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute the command passed to the container
|
||||||
|
exec "$@"
|
||||||
66
instructions.md
Normal file
66
instructions.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# BitNet LLM - Instructions
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
BitNet is a CPU-based large language model inference engine. This package provides SSH access so you can interact with the model directly.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **CPU with AVX2 support** (Intel Haswell or AMD Excavator or newer)
|
||||||
|
- Check support: SSH into your StartOS server and run `grep -o 'avx2' /proc/cpuinfo`
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
1. After installation, go to **Config** tab
|
||||||
|
2. Paste your SSH public key into the "SSH Authorized Keys" field
|
||||||
|
3. Save configuration
|
||||||
|
4. Restart the service
|
||||||
|
|
||||||
|
### Connecting via SSH
|
||||||
|
|
||||||
|
Get your Tor address from the **Interfaces** tab, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -o ProxyCommand="nc -x localhost:9050 %h %p" root@your-bitnet-address.onion
|
||||||
|
```
|
||||||
|
|
||||||
|
Or via LAN (if configured):
|
||||||
|
```bash
|
||||||
|
ssh root@your-server-ip -p <bitnet-ssh-port>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using BitNet
|
||||||
|
|
||||||
|
Once connected via SSH:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run inference with default model
|
||||||
|
python3 /BitNet/run_inference.py -m ggml-model-i2_s.gguf -p "Your prompt here"
|
||||||
|
|
||||||
|
# See all options
|
||||||
|
python3 /BitNet/run_inference.py --help
|
||||||
|
|
||||||
|
# Use custom model (mount in /root/models)
|
||||||
|
python3 /BitNet/run_inference.py -m /root/models/your-model.gguf -p "Your prompt"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Models
|
||||||
|
|
||||||
|
1. Download GGUF models from Hugging Face
|
||||||
|
2. Upload them via File Browser or SCP to `/root/models/`
|
||||||
|
3. Reference them in your inference commands
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
This runs entirely on CPU using AVX2 instructions. Performance depends on your server's CPU speed and core count. The default 2B parameter model is lightweight and should run reasonably well on modern hardware.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **Can't connect via SSH**: Verify your public key is correctly configured
|
||||||
|
- **"Illegal instruction" errors**: Your CPU doesn't support AVX2
|
||||||
|
- **Slow inference**: Normal for CPU-based inference; consider a smaller model or faster CPU
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- Upstream: https://github.com/microsoft/BitNet
|
||||||
|
- Container: https://github.com/kth8/bitnet
|
||||||
121
manifest.yaml
Normal file
121
manifest.yaml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
id: bitnet
|
||||||
|
version: 1.0.0
|
||||||
|
title: BitNet LLM
|
||||||
|
description:
|
||||||
|
short: Run BitNet LLM inference on CPU
|
||||||
|
long: |
|
||||||
|
BitNet is a CPU-based large language model inference engine from Microsoft.
|
||||||
|
This package runs the bitnet-b1.58-2B-4T model and provides SSH access for
|
||||||
|
interactive use. Requires AVX2 CPU support (Intel Haswell or newer).
|
||||||
|
|
||||||
|
release-notes: |
|
||||||
|
Initial release with SSH access enabled.
|
||||||
|
|
||||||
|
license: Apache-2.0
|
||||||
|
wrapper-repo: https://github.com/kth8/bitnet
|
||||||
|
upstream-repo: https://github.com/microsoft/BitNet
|
||||||
|
support-site: https://github.com/kth8/bitnet/issues
|
||||||
|
|
||||||
|
# No dependencies on other packages
|
||||||
|
dependencies: {}
|
||||||
|
|
||||||
|
# Docker image configuration
|
||||||
|
containers:
|
||||||
|
main:
|
||||||
|
image: main
|
||||||
|
mounts:
|
||||||
|
main: /root
|
||||||
|
|
||||||
|
# Volume for persistent data (models, etc.)
|
||||||
|
volumes:
|
||||||
|
main:
|
||||||
|
type: data
|
||||||
|
|
||||||
|
# Network interfaces
|
||||||
|
interfaces:
|
||||||
|
ssh:
|
||||||
|
name: SSH
|
||||||
|
description: SSH access to BitNet container
|
||||||
|
tor-config:
|
||||||
|
port-mapping:
|
||||||
|
22: "22"
|
||||||
|
lan-config:
|
||||||
|
443:
|
||||||
|
ssl: false
|
||||||
|
internal: 22
|
||||||
|
ui: false
|
||||||
|
protocols:
|
||||||
|
- tcp
|
||||||
|
- http
|
||||||
|
|
||||||
|
# Configuration options
|
||||||
|
config:
|
||||||
|
get:
|
||||||
|
type: script
|
||||||
|
set:
|
||||||
|
type: script
|
||||||
|
|
||||||
|
properties:
|
||||||
|
type: script
|
||||||
|
|
||||||
|
# Health check - verify sshd is running
|
||||||
|
health-checks:
|
||||||
|
main:
|
||||||
|
name: SSH Service
|
||||||
|
success-message: SSH server is running and ready for connections
|
||||||
|
type: script
|
||||||
|
|
||||||
|
# Actions for the user
|
||||||
|
actions:
|
||||||
|
run-inference:
|
||||||
|
name: Run Inference
|
||||||
|
description: Run a test inference query
|
||||||
|
warning: null
|
||||||
|
allowed-statuses:
|
||||||
|
- running
|
||||||
|
implementation:
|
||||||
|
type: docker
|
||||||
|
image: main
|
||||||
|
system: false
|
||||||
|
entrypoint: python3
|
||||||
|
args:
|
||||||
|
- /BitNet/run_inference.py
|
||||||
|
- -m
|
||||||
|
- ggml-model-i2_s.gguf
|
||||||
|
- -p
|
||||||
|
- "What is the meaning of life?"
|
||||||
|
mounts:
|
||||||
|
main: /root
|
||||||
|
io-format: json
|
||||||
|
|
||||||
|
backup:
|
||||||
|
create:
|
||||||
|
type: docker
|
||||||
|
image: compat
|
||||||
|
system: true
|
||||||
|
entrypoint: compat
|
||||||
|
args:
|
||||||
|
- duplicity
|
||||||
|
- create
|
||||||
|
- /mnt/backup
|
||||||
|
- /root/data
|
||||||
|
mounts:
|
||||||
|
BACKUP: /mnt/backup
|
||||||
|
main: /root/data
|
||||||
|
restore:
|
||||||
|
type: docker
|
||||||
|
image: compat
|
||||||
|
system: true
|
||||||
|
entrypoint: compat
|
||||||
|
args:
|
||||||
|
- duplicity
|
||||||
|
- restore
|
||||||
|
- /mnt/backup
|
||||||
|
- /root/data
|
||||||
|
mounts:
|
||||||
|
BACKUP: /mnt/backup
|
||||||
|
main: /root/data
|
||||||
|
|
||||||
|
migrations:
|
||||||
|
from: {}
|
||||||
|
to: {}
|
||||||
16
scripts/config_get.sh
Executable file
16
scripts/config_get.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Return current configuration as JSON
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"ssh-keys": {
|
||||||
|
"type": "textarea",
|
||||||
|
"name": "SSH Authorized Keys",
|
||||||
|
"description": "Paste your SSH public key(s) here, one per line",
|
||||||
|
"nullable": false,
|
||||||
|
"default": "",
|
||||||
|
"masked": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
12
scripts/config_set.sh
Executable file
12
scripts/config_set.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Read config from stdin
|
||||||
|
SSH_KEYS=$(jq -r '.["ssh-keys"]' /dev/stdin)
|
||||||
|
|
||||||
|
# Save to persistent volume
|
||||||
|
mkdir -p /root/config
|
||||||
|
echo "$SSH_KEYS" > /root/config/authorized_keys
|
||||||
|
|
||||||
|
# Return success
|
||||||
|
echo "{}"
|
||||||
10
scripts/health_check.sh
Executable file
10
scripts/health_check.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Check if SSH daemon is running
|
||||||
|
if pgrep -x sshd > /dev/null; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "SSH daemon is not running"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
27
scripts/properties.sh
Executable file
27
scripts/properties.sh
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Return package properties as JSON
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"data": {
|
||||||
|
"Model": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "bitnet-b1.58-2B-4T",
|
||||||
|
"description": "Default LLM model",
|
||||||
|
"copyable": false,
|
||||||
|
"qr": false,
|
||||||
|
"masked": false
|
||||||
|
},
|
||||||
|
"CPU Requirements": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "AVX2 support required",
|
||||||
|
"description": "Intel Haswell or AMD Excavator or newer",
|
||||||
|
"copyable": false,
|
||||||
|
"qr": false,
|
||||||
|
"masked": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
Reference in New Issue
Block a user