Back to .md Directory
JusPri - System Architecture Document
JusPri is a cloud-to-edge printing ecosystem with Bluetooth-based file transfer, pull-based authorization, and multi-format document support.
ai agent
View sourceJusPri - System Architecture Document
Overview
JusPri is a cloud-to-edge printing ecosystem with Bluetooth-based file transfer, pull-based authorization, and multi-format document support.
Core Principles
- Security First: Pi only executes after explicit cloud authorization
- Bluetooth Transfer: Files sent directly to Pi, not cloud
- Multi-Format Support: PDF, DOC/DOCX, TXT, MD, Images
- Pull-Based Model: Pi polls for authorization, not pushed
- Google OAuth: Single sign-on for users
- Session Scoped: Files deleted after job completion
Technology Stack
Cloud Backend
- Runtime: Node.js 18+
- Framework: Express.js
- Database: SQLite (dev) / PostgreSQL (prod)
- Auth: Google OAuth 2.0 via Passport.js
- Real-time: Socket.io (optional, polling primary)
- Host: AWS EC2 / Amplify
Frontend (Unified)
- Framework: React 18 + Vite
- Routing: React Router v6
- UI: Tailwind CSS + shadcn/ui
- State: React Query for API calls
- Apps: 3 routes (Client, Admin, Kiosk)
Pi Agent (Edge Node)
- Runtime: Node.js 18+
- Bluetooth: noble (BLE) / bleno
- Printing: CUPS (lp command)
- Conversion:
- LibreOffice (DOC/DOCX → PDF)
- ImageMagick (Images → PDF)
- Markdown → PDF (markdown-pdf)
Architecture Diagram
┌──────────────────────────────────────────────────────────────┐
│ CLOUD BACKEND (AWS) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Web App (React - 3 Routes) │ │
│ │ • /client - User mobile UI │ │
│ │ • /admin - Admin dashboard │ │
│ │ • /kiosk - Pi display │ │
│ └─────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼──────────────────────────────────┐ │
│ │ API Server (Express + Socket.io) │ │
│ │ • Google OAuth │ │
│ │ • Job Registry │ │
│ │ • Authorization Logic │ │
│ └─────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼──────────────────────────────────┐ │
│ │ Database (PostgreSQL) │ │
│ │ • Users, Jobs, Nodes, Sessions │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
▲
│ HTTPS API
│ (Polling/WebSocket)
│
┌────────────────────────▼─────────────────────────────────────┐
│ RASPBERRY PI (Edge Node) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ QR Code Display (node_id) │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Bluetooth Server (BLE) │ │
│ │ • Receives files from mobile │ │
│ │ • Creates job on cloud │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Document Converter │ │
│ │ • DOC/DOCX → PDF (LibreOffice) │ │
│ │ • Images → PDF (ImageMagick) │ │
│ │ • TXT/MD → PDF │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Authorization Poller │ │
│ │ • Polls cloud for job status │ │
│ │ • Waits for PAID/READY state │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Printer Controller (CUPS) │ │
│ │ • Executes authorized jobs │ │
│ │ • Reports status back │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│ USB
▼
┌──────────┐
│ Printer │
└──────────┘
Workflow (User Journey)
1. Discovery
User → Scans QR on Pi
→ Opens: https://juspri.com/client?node_id=pi_abc123
2. Authentication
User → Clicks "Login with Google"
→ Redirected to Google OAuth
→ Returns with auth token
3. File Transfer (Bluetooth)
User → Selects file on phone
→ App connects to Pi via BLE
→ File transferred directly to Pi
→ Pi receives file, generates job_id
4. Job Registration
Pi → POST /api/jobs/create
Body: { node_id, user_id, filename, file_size }
Cloud → Creates job entry (status: CREATED)
→ Returns: { job_id, pages, cost }
5. User Configuration
User → Sees job details on web app
→ Selects: color/bw, copies, etc.
→ Clicks "Pay ₹X"
6. Payment (Future)
User → Razorpay checkout
Cloud → Verifies payment
→ Updates job: status = PAID
7. Authorization Polling
Pi → GET /api/jobs/{job_id}/status (every 3s)
Cloud → Returns: { status: "PAID", print_config: {...} }
Pi → Sees status = PAID
→ Proceeds to print
8. Document Conversion
Pi → Checks file type
→ If DOC/DOCX: LibreOffice → PDF
→ If Image: ImageMagick → PDF
→ If TXT/MD: markdown-pdf → PDF
→ If PDF: Use directly
9. Printing
Pi → POST /api/jobs/{job_id}/status
Body: { status: "PRINTING" }
→ lp -d printer_name converted.pdf
→ POST /api/jobs/{job_id}/status
Body: { status: "COMPLETED", pages_printed: 5 }
10. Cleanup
Pi → Deletes local files
→ Removes from temp directory
→ Session complete
Database Schema
Users Table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
google_id VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
avatar_url TEXT,
created_at TIMESTAMP DEFAULT NOW(),
last_login TIMESTAMP
);
Nodes Table (Printers)
CREATE TABLE nodes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(255),
location VARCHAR(255),
bluetooth_address VARCHAR(17),
status VARCHAR(20) DEFAULT 'offline',
last_seen TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
Jobs Table
CREATE TABLE jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
job_id VARCHAR(100) UNIQUE NOT NULL,
user_id UUID REFERENCES users(id),
node_id VARCHAR(50) REFERENCES nodes(node_id),
-- File info
filename VARCHAR(255) NOT NULL,
original_format VARCHAR(10),
file_size INTEGER,
pages INTEGER,
-- Print config
color BOOLEAN DEFAULT false,
copies INTEGER DEFAULT 1,
duplex BOOLEAN DEFAULT false,
-- Cost
cost_per_page DECIMAL(10,2) DEFAULT 3.00,
total_cost DECIMAL(10,2),
-- Payment
payment_id VARCHAR(100),
payment_status VARCHAR(20) DEFAULT 'pending',
paid_at TIMESTAMP,
-- Status
status VARCHAR(20) DEFAULT 'CREATED',
-- CREATED, PENDING, PAID, PRINTING, COMPLETED, ERROR
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
print_started_at TIMESTAMP,
print_completed_at TIMESTAMP,
-- Error handling
error_code VARCHAR(50),
error_message TEXT
);
Sessions Table
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
job_id VARCHAR(100) REFERENCES jobs(job_id),
session_token VARCHAR(255) UNIQUE NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
API Endpoints
Authentication
GET /auth/google - Initiate Google OAuth
GET /auth/google/callback - OAuth callback
GET /auth/me - Get current user
POST /auth/logout - Logout
Jobs
POST /api/jobs/create - Register new job
GET /api/jobs/:job_id - Get job details
GET /api/jobs/:job_id/status - Get job status (Pi polling)
POST /api/jobs/:job_id/status - Update job status (Pi reports)
POST /api/jobs/:job_id/config - Update print config
POST /api/jobs/:job_id/payment - Verify payment
GET /api/jobs/user/:user_id - Get user's jobs
Nodes
GET /api/nodes/:node_id - Get node info
POST /api/nodes/:node_id/status - Update node status (heartbeat)
GET /api/nodes - List all nodes (admin)
Admin
GET /api/admin/jobs - List all jobs
GET /api/admin/stats - System statistics
POST /api/admin/jobs/:job_id/cancel - Cancel job
Security Model
Session Scoping
- Each job has unique session_token
- Token expires after 30 minutes
- Files deleted after job completion or timeout
Authorization Flow
1. User authenticates → Google OAuth token
2. Job created → session_token generated
3. Pi polls → uses session_token
4. Payment verified → status = PAID
5. Pi authorized → executes print
6. Completion → session expires, files deleted
Error Recovery
Network Lost:
- Pi caches job state locally
- Retries when connection restored
- No print without cloud authorization
Printer Error:
- Pi reports error_code to cloud
- Cloud marks job as ERROR
- Admin can retry or refund
File Format Support
| Format | Conversion Method | Notes |
|---|---|---|
| Direct | No conversion needed | |
| DOC/DOCX | LibreOffice | libreoffice --headless --convert-to pdf |
| TXT | markdown-pdf | Plain text → PDF |
| MD | markdown-pdf | Markdown → formatted PDF |
| PNG/JPG | ImageMagick | convert image.jpg output.pdf |
| Multiple Images | ImageMagick | Combine into single PDF |
Next Steps
- Backend Setup - Express + PostgreSQL + OAuth
- Frontend Setup - React + 3 routes + shared components
- Pi Agent - Bluetooth + Converters + CUPS
- Testing - End-to-end flow
- Deployment - AWS + Pi configuration
Ready to build JusPri! 🚀
Related Documents
AGENTS.md
Browser-only development
This document provides guidance for AI assistants working on the Image MetaHub codebase.
airagprompt
0
3
LuqP2AGENTS.md
Claude Agents — Reference & Recommendations
Quick guide to available agents. Pick the one that best matches your task.
aiagentrag
0
474
magda2307AGENTS.md
Golden DKG Prototype -- Master Plan
Rust prototype of the Golden non-interactive Distributed Key Generation protocol.
aiagenteval
0
83
farazshaikhAGENTS.md
Swarms Examples Index
A comprehensive index of examples from the [Swarms Framework](https://github.com/The-Swarm-Corporation/swarms-examples).
aiagentopenai
0
62
The-Swarm-Corporation