chore: initialize monorepo with project scaffolding, configs, and CI setup

This commit is contained in:
oib
2025-09-27 06:05:25 +02:00
commit c1926136fb
171 changed files with 13708 additions and 0 deletions

View File

@ -0,0 +1,600 @@
# AITBC Artificial Intelligence Token Blockchain
## Overview (Recovered)
- AITBC couples decentralized blockchain control with assetbacked value derived from AI computation.
- No premint: tokens are minted by providers **only after** serving compute; prices are set by providers (can be free at bootstrap).
## Staged Development Roadmap
### Stage 1: ClientServer Prototype (no blockchain, no hub)
- Direct client → server API.
- APIkey auth; local job logging.
- Goal: validate AI service loop and throughput.
### Stage 2: Blockchain Integration
- Introduce **AIToken** and minimal smart contracts for minting + accounting.
- Mint = amount of compute successfully served; no premint.
### Stage 3: AI Pool Hub
- Hub matches requests to multiple servers (sharding/parallelization), verifies outputs, and accounts contributions.
- Distributes payments/minted tokens proportionally to work.
### Stage 4: Marketplace
- Web DEX/market to buy/sell AITokens; price discovery; reputation and SLAs.
## System Architecture: Actors
- **Client** requests AI jobs (e.g., image/video generation).
- **Server/Provider** runs models (Stable Diffusion, PyTorch, etc.).
- **Blockchain Node** ledger + minting rules.
- **AI Pool Hub** orchestration, metering, payouts.
## Token Minting Logic (Genesisless)
- No tokens at boot.
- Provider advertises price/unit (e.g., 1 AIToken per image or per N GPUseconds).
- After successful job → provider mints that amount. Free jobs mint 0.
---
# Stage 1 Technischer Implementierungsplan (Detail)
## Ziele
- Funktionierender EndtoEndPfad: Prompt → Inferenz → Ergebnis.
- Authentifizierung, RateLimit, Logging.
## Architektur
```
[ Client ] ⇄ HTTP/JSON ⇄ [ FastAPI AIServer (GPU) ]
```
- Server hostet Inferenz-Endpunkte; Client sendet Aufträge.
- Optional: WebSocket für StreamingLogs/Progress.
## TechnologieStack
- **Server**: Python 3.10+, FastAPI, Uvicorn, PyTorch, diffusers (Stable Diffusion), PIL.
- **Client**: Python CLI (requests / httpx) oder schlankes WebUI.
- **Persistenz**: SQLite oder JSON Log; Artefakte auf Disk/S3ähnlich.
- **Sicherheit**: APIKey (env/secret file), CORS policy, RateLimit (slowapi), timeouts.
## APISpezifikation (v0)
### POST `/v1/generate-image`
Request JSON:
```json
{
"api_key": "<KEY>",
"prompt": "a futuristic city skyline at night",
"steps": 30,
"guidance": 7.5,
"width": 512,
"height": 512,
"seed": 12345
}
```
Response JSON:
```json
{
"status": "ok",
"job_id": "2025-09-26-000123",
"image_base64": "data:image/png;base64,....",
"duration_ms": 2180,
"gpu_seconds": 1.9
}
```
### GET `/v1/health`
- Rückgabe von `{ "status": "ok", "gpu": "RTX 2060", "model": "SD1.5" }`.
## ServerAblauf (Pseudocode)
```python
@app.post("/v1/generate-image")
def gen(req: Request):
assert check_api_key(req.api_key)
rate_limit(req.key)
t0 = now()
img = stable_diffusion.generate(prompt=req.prompt, ...)
log_job(user=req.key, gpu_seconds=measure_gpu(), ok=True)
return {"status":"ok", "image_base64": b64(img), "duration_ms": ms_since(t0)}
```
## Betriebliche Aspekte
- **Logging**: strukturierte Logs (JSON) inkl. PromptHash, Laufzeit, GPUSekunden, ExitCode.
- **Observability**: Prometheus/OpenTelemetryMetriken (req/sec, p95 Latenz, VRAMNutzung).
- **Fehler**: RetryPolicy (idempotent), Graceful shutdown, Max batch/queue size.
- **Sicherheit**: InputSanitization, UploadLimits, tmpVerzeichnis säubern.
## SetupSchritte (Linux, NVIDIA RTX 2060)
```bash
sudo apt update && sudo apt install -y python3-venv git
python3 -m venv venv && source venv/bin/activate
pip install fastapi uvicorn[standard] torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install diffusers transformers accelerate pillow safetensors xformers slowapi httpx
# Start
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 1
```
## Akzeptanzkriterien
- `GET /v1/health` liefert GPU/ModelInfos.
- `POST /v1/generate-image` liefert innerhalb < 5s ein 512×512 PNG (bei RTX 2060, SD1.5, \~30 steps).
- Logs enthalten pro Job mindestens: job\_id, duration\_ms, gpu\_seconds, bytes\_out.
## Nächste Schritte zu Stage 2
- JobQuittungsschema definieren (hashbare Receipt für OnChainMint später).
- Einheit ComputeEinheit festlegen (z.B. GPUSekunden, Token/Prompt).
- Nonce/Signatur im Request zur späteren OnChainVerifikation.
---
## Kurze Stage2/3/4Vorschau (Implementierungsnotizen)
- **Stage 2 (Blockchain)**: Smart Contract mit `mint(provider, units, receipt_hash)`, OffChainOrakel/Attester.
- **Stage 3 (Hub)**: Scheduler (priority, price, reputation), Sharding großer Jobs, KonsistenzChecks, RewardSplit.
- **Stage 4 (Marketplace)**: Orderbook, KYC/Compliance Layer (jurisdictions), Custodyfreie WalletAnbindung.
## Quellen (Auszug)
- Ethereum Smart Contracts: [https://ethereum.org/en/smart-contracts/](https://ethereum.org/en/smart-contracts/)
- PoS Überblick: [https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/)
- PyTorch Deploy: [https://pytorch.org/tutorials/](https://pytorch.org/tutorials/)
- FastAPI Docs: [https://fastapi.tiangolo.com/](https://fastapi.tiangolo.com/)
---
# Stage 1 ReferenzImplementierung (Code)
## `.env`
```
API_KEY=CHANGE_ME_SUPERSECRET
MODEL_ID=runwayml/stable-diffusion-v1-5
BIND_HOST=0.0.0.0
BIND_PORT=8000
```
## `requirements.txt`
```
fastapi
uvicorn[standard]
httpx
pydantic
python-dotenv
slowapi
pillow
torch
torchvision
torchaudio
transformers
diffusers
accelerate
safetensors
xformers
```
## `server.py`
```python
import base64, io, os, time, hashlib
from functools import lru_cache
from typing import Optional
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, Field
from slowapi import Limiter
from slowapi.util import get_remote_address
from PIL import Image
load_dotenv()
API_KEY = os.getenv("API_KEY", "CHANGE_ME_SUPERSECRET")
MODEL_ID = os.getenv("MODEL_ID", "runwayml/stable-diffusion-v1-5")
app = FastAPI(title="AITBC Stage1 Server", version="0.1.0")
limiter = Limiter(key_func=get_remote_address)
class GenRequest(BaseModel):
api_key: str
prompt: str
steps: int = Field(30, ge=5, le=100)
guidance: float = Field(7.5, ge=0, le=25)
width: int = Field(512, ge=256, le=1024)
height: int = Field(512, ge=256, le=1024)
seed: Optional[int] = None
@lru_cache(maxsize=1)
def load_pipeline():
from diffusers import StableDiffusionPipeline
import torch
pipe = StableDiffusionPipeline.from_pretrained(MODEL_ID, torch_dtype=torch.float16, safety_checker=None)
pipe = pipe.to("cuda" if torch.cuda.is_available() else "cpu")
pipe.enable_attention_slicing()
return pipe
@app.get("/v1/health")
def health():
gpu = os.getenv("NVIDIA_VISIBLE_DEVICES", "auto")
return {"status": "ok", "gpu": gpu, "model": MODEL_ID}
@app.post("/v1/generate-image")
@limiter.limit("10/minute")
def generate(req: GenRequest, request: Request):
if req.api_key != API_KEY:
raise HTTPException(status_code=401, detail="invalid api_key")
t0 = time.time()
pipe = load_pipeline()
generator = None
if req.seed is not None:
import torch
generator = torch.Generator(device=pipe.device).manual_seed(int(req.seed))
result = pipe(req.prompt, num_inference_steps=req.steps, guidance_scale=req.guidance, width=req.width, height=req.height, generator=generator)
img: Image.Image = result.images[0]
buf = io.BytesIO()
img.save(buf, format="PNG")
b64 = base64.b64encode(buf.getvalue()).decode("ascii")
dur_ms = int((time.time() - t0) * 1000)
job_id = hashlib.sha256(f"{t0}-{req.prompt[:64]}".encode()).hexdigest()[:16]
log_line = {"job_id": job_id, "duration_ms": dur_ms, "bytes_out": len(b64), "prompt_hash": hashlib.sha256(req.prompt.encode()).hexdigest()}
print(log_line, flush=True)
return {"status": "ok", "job_id": job_id, "image_base64": f"data:image/png;base64,{b64}", "duration_ms": dur_ms}
if __name__ == "__main__":
import uvicorn, os
uvicorn.run("server:app", host=os.getenv("BIND_HOST", "0.0.0.0"), port=int(os.getenv("BIND_PORT", "8000")), reload=False)
```
## `client.py`
```python
import base64, json, os
import httpx
API = os.getenv("API", "http://localhost:8000")
API_KEY = os.getenv("API_KEY", "CHANGE_ME_SUPERSECRET")
payload = {
"api_key": API_KEY,
"prompt": "a futuristic city skyline at night, ultra detailed, neon",
"steps": 30,
"guidance": 7.5,
"width": 512,
"height": 512,
}
r = httpx.post(f"{API}/v1/generate-image", json=payload, timeout=120)
r.raise_for_status()
resp = r.json()
print("job:", resp.get("job_id"), "duration_ms:", resp.get("duration_ms"))
img_b64 = resp["image_base64"].split(",",1)[1]
open("out.png","wb").write(base64.b64decode(img_b64))
print("saved out.png")
```
---
# OpenAPI 3.1 Spezifikation (Stage 1)
```yaml
openapi: 3.1.0
info:
title: AITBC Stage1 Server
version: 0.1.0
servers:
- url: http://localhost:8000
paths:
/v1/health:
get:
summary: Health check
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
status: { type: string }
gpu: { type: string }
model: { type: string }
/v1/generate-image:
post:
summary: Generate image from text prompt
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [api_key, prompt]
properties:
api_key: { type: string }
prompt: { type: string }
steps: { type: integer, minimum: 5, maximum: 100, default: 30 }
guidance: { type: number, minimum: 0, maximum: 25, default: 7.5 }
width: { type: integer, minimum: 256, maximum: 1024, default: 512 }
height: { type: integer, minimum: 256, maximum: 1024, default: 512 }
seed: { type: integer, nullable: true }
responses:
'200':
description: Image generated
content:
application/json:
schema:
type: object
properties:
status: { type: string }
job_id: { type: string }
image_base64: { type: string }
duration_ms: { type: integer }
```
---
# Stage 2 Receipt/QuittungsSchema & Hashing
## JSON Receipt (offchain, signierbar)
```json
{
"job_id": "2025-09-26-000123",
"provider": "0xProviderAddress",
"client": "client_public_key_or_id",
"units": 1.90,
"unit_type": "gpu_seconds",
"model": "runwayml/stable-diffusion-v1-5",
"prompt_hash": "sha256:...",
"started_at": 1695720000,
"finished_at": 1695720002,
"artifact_sha256": "...",
"nonce": "b7f3...",
"hub_id": "optional-hub",
"chain_id": 11155111
}
```
## Hashing
- Kanonische Serialisierung (minified JSON, Felder in alphabetischer Reihenfolge).
- `receipt_hash = keccak256(bytes(serialized))` (für EVMKompatibilität) **oder** `sha256` falls kettenagnostisch.
## Signatur
- Signatur über `receipt_hash`:
- **secp256k1/ECDSA** (Ethereumkompatibel, EIP191/EIP712) **oder** Ed25519 (falls OffChainAttester bevorzugt).
- Felder zur Verifikation onchain: `provider`, `units`, `receipt_hash`, `signature`.
## DoubleMintPrevention
- Smart Contract speichert `used[receipt_hash] = true` nach erfolgreichem Mint.
---
# Stage 2 SmartContractSkeleton (Solidity)
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IERC20Mint {
function mint(address to, uint256 amount) external;
}
contract AITokenMinter {
IERC20Mint public token;
address public attester; // Offchain Hub/Oracle, darf Quittungen bescheinigen
mapping(bytes32 => bool) public usedReceipt; // receipt_hash → consumed
event Minted(address indexed provider, uint256 units, bytes32 receiptHash);
event AttesterChanged(address indexed oldA, address indexed newA);
constructor(address _token, address _attester) {
token = IERC20Mint(_token);
attester = _attester;
}
function setAttester(address _attester) external /* add access control */ {
emit AttesterChanged(attester, _attester);
attester = _attester;
}
function mintWithReceipt(
address provider,
uint256 units,
bytes32 receiptHash,
bytes calldata attesterSig
) external {
require(!usedReceipt[receiptHash], "receipt used");
// Verify attester signature over EIP191 style message: keccak256(abi.encode(provider, units, receiptHash))
bytes32 msgHash = keccak256(abi.encode(provider, units, receiptHash));
require(_recover(msgHash, attesterSig) == attester, "bad sig");
usedReceipt[receiptHash] = true;
token.mint(provider, units);
emit Minted(provider, units, receiptHash);
}
function _recover(bytes32 msgHash, bytes memory sig) internal pure returns (address) {
bytes32 ethHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", msgHash));
(bytes32 r, bytes32 s, uint8 v) = _split(sig);
return ecrecover(ethHash, v, r, s);
}
function _split(bytes memory sig) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
require(sig.length == 65, "sig len");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}
```
> Hinweis: In Produktion AccessControl (Ownable/Rolebased), Pausable, ReentrancyGuard und EIP712TypedData einführen.
---
# Stage 3 HubSpezifikation (Kurz)
- **Scheduler**: Roundrobin + Preis/VRAMFilter; optional Reputation.
- **Split**: Große Jobs sharden; `units` aus Subjobs aggregieren.
- **Verification**: Stichprobenhafte ReAuswertung / KonsistenzHashes.
- **Payout**: Proportionale Verteilung; ein Receipt je Gesamtjob.
# Stage 4 Marketplace (Kurz)
- **Orderbook** (limit/market), **WalletConnect**, Noncustodial.
- **KYC/Compliance** optional je Jurisdiktion.
- **Reputation/SLAs** on/offchain verknüpfbar.
---
# Deployment ohne Docker (BareMetal / VM)
## Voraussetzungen
- Ubuntu/Debian mit NVIDIA Treiber (535+) und CUDA/CuDNN passend zur PyTorchVersion.
- Python 3.10+ und `python3-venv`.
- Öffentliche Ports: **8000/tcp** (API) optional ReverseProxy auf 80/443.
## Treiber & CUDA (Kurz)
```bash
# NVIDIA Treiber (Beispiel Ubuntu)
sudo apt update && sudo apt install -y nvidia-driver-535
# Nach Reboot: nvidia-smi prüfen
# PyTorch bringt eigenes CUDA-Toolkit über Wheels (empfohlen). Kein System-CUDA zwingend nötig.
```
## Benutzer & Verzeichnisstruktur
```bash
sudo useradd -m -r -s /bin/bash aitbc
sudo -u aitbc mkdir -p /opt/aitbc/app /opt/aitbc/logs
# Code nach /opt/aitbc/app kopieren
```
## Virtualenv & Abhängigkeiten
```bash
sudo -u aitbc bash -lc '
cd /opt/aitbc/app && python3 -m venv venv && source venv/bin/activate && \
pip install --upgrade pip && pip install -r requirements.txt
'
```
## Konfiguration (.env)
```
API_KEY=<GEHEIM>
MODEL_ID=runwayml/stable-diffusion-v1-5
BIND_HOST=127.0.0.1 # hinter Reverse Proxy
BIND_PORT=8000
```
## SystemdUnit (Uvicorn)
`/etc/systemd/system/aitbc.service`
```ini
[Unit]
Description=AITBC Stage1 FastAPI Server
After=network-online.target
Wants=network-online.target
[Service]
User=aitbc
Group=aitbc
WorkingDirectory=/opt/aitbc/app
EnvironmentFile=/opt/aitbc/app/.env
ExecStart=/opt/aitbc/app/venv/bin/python -m uvicorn server:app --host ${BIND_HOST} --port ${BIND_PORT} --workers 1
Restart=always
RestartSec=3
# GPU/VRAM limits optional per nvidia-visible-devices
StandardOutput=append:/opt/aitbc/logs/stdout.log
StandardError=append:/opt/aitbc/logs/stderr.log
[Install]
WantedBy=multi-user.target
```
Aktivieren & Starten:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now aitbc.service
sudo systemctl status aitbc.service
```
## Reverse Proxy (optional, ohne Docker)
### Nginx (TLS via Certbot)
```bash
sudo apt install -y nginx certbot python3-certbot-nginx
sudo tee /etc/nginx/sites-available/aitbc <<'NG'
server {
listen 80; server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
NG
sudo ln -s /etc/nginx/sites-available/aitbc /etc/nginx/sites-enabled/aitbc
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.com
```
## Firewall/Netzwerk
```bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```
## Monitoring ohne Docker
- **systemd**: `journalctl -u aitbc -f`
- **Metriken**: Prometheus Node Exporter, `nvtop`/`nvidia-smi dmon` für GPU.
- **Alerts**: systemd `Restart=always`, optional Monit.
## ZeroDowntime Update (Rolling ohne Container)
```bash
sudo systemctl stop aitbc
sudo -u aitbc bash -lc 'cd /opt/aitbc/app && git pull && source venv/bin/activate && pip install -r requirements.txt'
sudo systemctl start aitbc
```
## Härtung & Best Practices
- Starker APIKey, IPbasierte AllowList am ReverseProxy.
- RateLimit (slowapi) aktivieren; RequestBodyLimits setzen (`client_max_body_size`).
- Temporäre Dateien regelmäßig bereinigen (systemd tmpfiles).
- Separate GPUWorkstation vs. EdgeExpose (API hinter Proxy).
> Hinweis: Diese Anleitung vermeidet bewusst jeglichen DockerEinsatz und nutzt **systemd + venv** für einen reproduzierbaren, schlanken Betrieb.