Version: 0.1 (Draft)
Date: March 2026
Status: Proposal
Depends on: Stroke Format Spec, Visual Primitives Spec
Implements: BOOX Edge, reMarkable Edge, iPad Edge, Web Edge
The Edge Contract defines what any Edge implementation must support to be a valid FreakingGenius "paper" device.
An Edge is dumb by design. It captures strokes, renders primitives, reports its state. It does not interpret math, make pedagogical decisions, or generate audio.
Edge is paper. Paper doesn't think.
This spec defines:
This spec does NOT define:
| Platform | Status | Notes |
|---|---|---|
| BOOX (Android e-ink) | Primary target | v1 |
| reMarkable 2 | Planned | Pending platform access |
| iPad | Future | High-refresh LCD |
| Web | Future | Fallback, reduced capability |
At session start, Edge sends its capabilities to Tutor (via Bridge):
{
"type": "CAPABILITIES",
"device": {
"id": "dev_abc123",
"platform": "boox",
"model": "Tab Ultra C",
"edgeVersion": "1.0.0",
"protocolVersion": 1
},
"display": {
"width": 2480,
"height": 1860,
"colorDepth": 16,
"refreshRate": "medium",
"colorSupport": true
},
"input": {
"stylus": true,
"stylusPressure": true,
"stylusTilt": true,
"touch": true,
"samplingRate": 120
},
"features": {
"audio": false,
"camera": false,
"haptics": false,
"offlineStorage": "2GB"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Unique device identifier |
platform |
enum | ✓ | boox, remarkable, ipad, android, web |
model |
string | ✓ | Device model name |
edgeVersion |
semver | ✓ | Edge app version |
protocolVersion |
int | ✓ | Contract version implemented |
| Field | Type | Required | Values |
|---|---|---|---|
width |
int | ✓ | Pixels |
height |
int | ✓ | Pixels |
colorDepth |
int | 1 (B&W), 4 (grayscale), 16 (color) | |
refreshRate |
enum | ✓ | slow (e-ink), medium, fast (LCD) |
colorSupport |
bool | ✓ | Can render color |
| Field | Type | Required | Description |
|---|---|---|---|
stylus |
bool | ✓ | Has stylus input |
stylusPressure |
bool | Pressure sensitivity | |
stylusTilt |
bool | Tilt detection | |
touch |
bool | Finger touch support | |
samplingRate |
int | Input samples per second |
| Field | Type | Description |
|---|---|---|
audio |
bool | Can play/record audio (rare for tablet Edge) |
camera |
bool | Has camera access |
haptics |
bool | Vibration feedback |
offlineStorage |
string | Available offline cache size |
┌─────────┐ ┌─────────┐
│ Edge │ │ Tutor │
└────┬────┘ └────┬────┘
│ │
│ CAPABILITIES │
│─────────────────────────────►│
│ │
│ SESSION_CONFIG │
│◄─────────────────────────────│
│ │
│ (session active) │
Tutor (via Bridge) responds with configuration based on capabilities:
{
"type": "SESSION_CONFIG",
"sessionId": "sess_xyz789",
"renderMode": {
"color": true,
"animations": false,
"refreshStrategy": "partial"
},
"strokeConfig": {
"batchIntervalMs": 200,
"format": "binary",
"includePressure": true
},
"features": {
"hintsEnabled": true,
"guidesEnabled": true
}
}
All messages wrapped in standard envelope:
{
"version": 1,
"timestamp": 1711670400000,
"type": "MESSAGE_TYPE",
"payload": { ... }
}
Sent at session start. See section 2.1.
Stroke data per Stroke Format Spec.
{
"type": "STROKE_BATCH",
"payload": {
"sessionId": "sess_xyz789",
"seq": 42,
"strokes": [ ... ],
"partial": { ... }
}
}
User gesture detected.
{
"type": "GESTURE",
"payload": {
"gesture": "undo",
"timestamp": 1711670450000
}
}
| Gesture | Meaning |
|---|---|
undo |
Undo last stroke |
redo |
Redo undone stroke |
erase_area |
Erased region (include bounds) |
next |
Request next exercise |
previous |
Request previous exercise |
submit |
Student indicates "done" |
help |
Student requests hint |
Periodic status (every 30s).
{
"type": "HEARTBEAT",
"payload": {
"sessionId": "sess_xyz789",
"battery": 78,
"connectivity": "strong",
"activeZone": "work_zone",
"strokeCount": 142,
"lastActivityMs": 1200
}
}
| Field | Type | Description |
|---|---|---|
battery |
int | Percentage (0-100) |
connectivity |
enum | strong, weak, offline |
activeZone |
string | Current INPUT_ZONE id, if any |
strokeCount |
int | Total strokes this session |
lastActivityMs |
int | Ms since last stroke |
Fired when student enters/exits an input zone.
{
"type": "ZONE_ACTIVITY",
"payload": {
"zoneId": "answer_zone",
"action": "enter",
"timestamp": 1711670500000
}
}
| Action | Meaning |
|---|---|
enter |
Stroke started in zone |
exit |
Stroke ended, left zone |
Edge encountered an error.
{
"type": "ERROR",
"payload": {
"code": "RENDER_FAILED",
"message": "Failed to render MATH_BLOCK: unsupported format",
"primitiveId": "math_001"
}
}
Session configuration. See section 2.3.
Load new exercise content.
{
"type": "LOAD_EXERCISE",
"payload": {
"exerciseId": "ex_4521",
"primitives": [ ... ],
"clearStrokes": true
}
}
| Field | Type | Description |
|---|---|---|
exerciseId |
string | Exercise identifier |
primitives |
array | Content primitives per Visual Primitives Spec |
clearStrokes |
bool | Clear existing strokes (default true) |
Single primitive command.
{
"type": "PRIMITIVE",
"payload": {
"type": "HIGHLIGHT",
"id": "hl_001",
...
}
}
Multiple primitives at once.
{
"type": "PRIMITIVE_BATCH",
"payload": {
"primitives": [ ... ]
}
}
Clear primitives or strokes.
{
"type": "CLEAR",
"payload": {
"target": "annotations"
}
}
| Target | Effect |
|---|---|
annotations |
Clear annotation layer |
hints |
Clear hint primitives |
feedback |
Clear marks, pulses |
strokes |
Clear student strokes |
all |
Full reset |
Acknowledge received strokes.
{
"type": "STROKE_ACK",
"payload": {
"ackSeq": 42
}
}
Edge can discard buffered batches ≤ ackSeq.
End the session.
{
"type": "SESSION_END",
"payload": {
"reason": "completed",
"summary": {
"duration": 2700,
"exercisesCompleted": 5,
"strokeCount": 487
}
}
}
| Reason | Meaning |
|---|---|
completed |
Normal end |
timeout |
Inactivity timeout |
error |
Error forced end |
user_exit |
User closed app |
┌─────────────┐
│ IDLE │
│ (app open, │
│ not paired) │
└──────┬──────┘
│ Pairing initiated
▼
┌─────────────┐
│ PAIRING │
│ │
└──────┬──────┘
│ CAPABILITIES sent, SESSION_CONFIG received
▼
┌─────────────┐
│ ACTIVE │◄─────────────────────┐
│ │ │
│ • Streaming │ LOAD_EXERCISE │
│ strokes │──────────────────────┘
│ • Rendering │
│ primitives│
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PAUSED │ │RECONNECT │ │ ENDED │
│ │ │ │ │ │
│ (user │ │ (conn │ │ (session │
│ paused) │ │ lost) │ │ over) │
└────┬─────┘ └────┬─────┘ └──────────┘
│ │
│ Resume │ Connection restored
└────────────┴───────► ACTIVE
| State | Description | Edge Behavior |
|---|---|---|
IDLE |
App open, no active session | Show pairing UI |
PAIRING |
Connecting to Tutor | Send CAPABILITIES, wait for config |
ACTIVE |
Session in progress | Stream strokes, render primitives |
PAUSED |
User paused session | Stop streaming, retain state |
RECONNECTING |
Connection lost | Buffer strokes, attempt reconnect |
ENDED |
Session complete | Show summary, return to IDLE |
When connection drops:
RECONNECTING stateresume: true)Edge MUST:
asciimath format for MATH_BLOCKshowBorder: trueEdge SHOULD:
latex format for MATH_BLOCKEdge MUST:
Edge SHOULD:
Edge MUST:
Edge SHOULD:
For e-ink displays:
| Scenario | Strategy |
|---|---|
| Stroke rendering | Fast/partial refresh (ghosting OK) |
| LOAD_EXERCISE | Full refresh |
| Annotation added | Partial refresh |
| TRANSITION received | Honor hint field |
| Idle > 30s | Full refresh to clear ghosting |
Edge MUST:
Edge MUST:
batchIntervalMsEdge MUST detect and report:
undo (platform-standard gesture or button)submit (explicit "done" action)Edge SHOULD detect:
help (request hint)next / previous (exercise navigation if enabled)Edge MUST:
Edge MAY:
| Element | Purpose |
|---|---|
| Connection indicator | Show paired/unpaired/reconnecting |
| Session status | Active exercise, time elapsed |
| Undo button | Trigger GESTURE:undo |
| Done button | Trigger GESTURE:submit |
| Element | Purpose |
|---|---|
| Hint button | Request hint (GESTURE:help) |
| Navigation | Previous/next exercise |
| Pause button | Pause session |
| Settings | Stylus calibration, preferences |
Edge implementations must pass:
For each new Edge platform:
| Version | Date | Changes |
|---|---|---|
| 1 | March 2026 | Initial spec |
| Type | Required | Description |
|---|---|---|
CAPABILITIES |
✓ | Device capabilities at session start |
STROKE_BATCH |
✓ | Batched stroke data |
GESTURE |
✓ | User gesture detected |
HEARTBEAT |
✓ | Periodic status |
ZONE_ACTIVITY |
Enter/exit input zone | |
ERROR |
Error report |
| Type | Required | Description |
|---|---|---|
SESSION_CONFIG |
✓ | Session configuration |
LOAD_EXERCISE |
✓ | Load exercise content |
PRIMITIVE |
✓ | Single visual primitive |
PRIMITIVE_BATCH |
✓ | Multiple primitives |
CLEAR |
✓ | Clear content/annotations |
STROKE_ACK |
✓ | Acknowledge strokes |
SESSION_END |
✓ | End session |
| Code | Meaning |
|---|---|
RENDER_FAILED |
Could not render primitive |
UNSUPPORTED_PRIMITIVE |
Unknown primitive type |
UNSUPPORTED_FORMAT |
Unknown math format |
STORAGE_FULL |
Local buffer exhausted |
CONNECTION_FAILED |
Could not connect to Tutor |
SESSION_EXPIRED |
Session timed out |
This completes Phase 1: Foundation specs. Next: Bridge Protocol (Phase 2).