Requirements overview
This section provides an overview of the requirements specification for Papyrus, including the structure, notation, and traceability between requirements, use cases, and entities.
Requirement notation
Identifiers
FR-X_Y - functional requirement (e.g., FR-2_1: format conversion )
NFR-X_Y - non-functional requirement (e.g., NFR-4_1: startup time )
UC-X_Y - use case (e.g., UC-2_1: import books ).
Priority levels
P0 - critical (v0.x).
P1 - high (v1.0).
P2 - medium (v1.x).
P3 - low (v2.x+).
All requirements
ID
Title
Status
Priority
FR-1_1
Account registration
open
P0
FR-1_2
Email/password login
open
P0
FR-1_3
OAuth login (Google)
open
P1
FR-1_4
Offline mode
open
P0
FR-1_5
Password recovery
open
P1
FR-1_6
Account deletion
open
P1
FR-2_1
Format conversion
open
P2
FR-2_10
Book tagging
open
P0
FR-2_11
Query language filters
open
P2
FR-2_12
ISBN barcode scanning
open
P2
FR-2_13
Metadata fetching
open
P1
FR-2_14
Physical book tracking
open
P1
FR-2_2
Manual metadata editing
open
P0
FR-2_3
Custom metadata fields
open
P2
FR-2_4
Book file export
open
P0
FR-2_5
Library data export
open
P1
FR-2_6
Book import
open
P0
FR-2_7
Full-text search
open
P1
FR-2_8
Advanced search
open
P0
FR-2_9
Shelf organization
open
P0
FR-3_1
E-book reading
open
P0
FR-3_2
Viewer customization
open
P0
FR-3_3
Reading profiles
open
P1
FR-3_4
Bookmarks
open
P0
FR-4_1
Text highlighting
open
P0
FR-4_2
Book notes
open
P0
FR-4_3
Annotation editing
open
P0
FR-4_4
Annotation export
open
P1
FR-4_5
Annotation search
open
P1
FR-5_1
Reading statistics
open
P0
FR-5_2
Statistics filtering
open
P1
FR-5_3
Cross-device sync
open
P0
FR-6_1
Reading goals
open
P1
FR-6_2
Manual goal updates
open
P1
FR-6_3
Automatic goal progress
open
P1
FR-6_4
Goal notifications
open
P2
FR-7_1
File storage backend selection
open
P0
FR-7_1_1
Metadata server configuration
open
P0
FR-7_2
File upload
open
P0
FR-7_3
OCR processing
open
P2
FR-7_4
Plugin system
open
P3
FR-7_5
OPDS catalog support
open
P2
NFR-1_1
Maximum file size
open
P0
NFR-1_2
Storage backend support
open
P0
NFR-1_3
Multi-backend configuration
open
P1
NFR-1_4
File encryption
open
P2
NFR-2_1
Online/offline parity
open
P0
NFR-2_2
Offline change indicators
open
P0
NFR-2_3
Conflict resolution
open
P1
NFR-2_4
Sync performance
open
P0
NFR-3_1
Web browser support
open
P0
NFR-3_2
Desktop operating system support
open
P0
NFR-3_3
Mobile operating system support
open
P0
NFR-3_4
E-ink device support
open
P1
NFR-4_1
Application startup
open
P0
NFR-4_2
Book opening
open
P0
NFR-4_3
Search performance
open
P0
NFR-4_4
Library scalability
open
P1
NFR-4_5
E-ink performance
open
P1
NFR-5_1
Design system
open
P0
NFR-5_2
Accessibility
open
P0
NFR-5_3
RTL and internationalization
open
P1
NFR-5_4
Localization
open
P1
NFR-5_5
E-ink usability
open
P1
NFR-6_1
Password security
open
P0
NFR-6_2
Transport security
open
P0
NFR-6_3
Two-factor authentication
open
P2
NFR-6_4
Session management
open
P0
NFR-6_5
Privacy by default
open
P0
NFR-7_1
System uptime
open
P1
NFR-7_2
Data integrity
open
P0
NFR-7_3
Network resilience
open
P0
NFR-7_4
Backup and restore
open
P1
NFR-8_1
Plugin architecture
open
P3
NFR-9_1
Code quality
open
P1
NFR-9_2
Logging and monitoring
open
P1
UC-1_1
Create user account
open
P0
UC-1_2
Login with credentials
open
P0
UC-1_3
Login with Google account
open
P1
UC-1_4
Use application offline
open
P0
UC-1_5
Recover password
open
P1
UC-1_6
Delete account
open
P1
UC-2_1
Import book files
open
P0
UC-2_2
Convert book formats
open
P2
UC-2_3
Edit book metadata
open
P0
UC-2_4
Organize books into shelves
open
P0
UC-2_5
Tag books
open
P0
UC-2_6
Search books
open
P0
UC-2_7
Export books and data
open
P0
UC-2_8
Scan ISBN barcode
open
P2
UC-2_9
Track physical book
open
P1
UC-3_1
Read books with integrated viewer
open
P0
UC-3_2
Customize reading experience
open
P0
UC-3_3
Manage reading profiles
open
P1
UC-3_4
Manage bookmarks
open
P0
UC-4_1
Create text annotations
open
P0
UC-4_2
Create book notes
open
P0
UC-4_3
Manage annotations
open
P0
UC-4_4
Export annotations
open
P1
UC-4_5
Search annotations
open
P1
UC-5_1
Track reading progress
open
P0
UC-5_2
Filter progress statistics
open
P1
UC-6_1
Create reading goals
open
P1
UC-6_2
Manage goal progress
open
P1
UC-7_1
Configure storage options
open
P0
UC-7_2
Process scanned documents
open
P2
UC-7_3
Synchronize data across devices
open
P0
UC-7_4
Browse OPDS catalog
open
P2
Functional requirements by category
Category
Scope
Description
User management
FR-1.x
Account, auth, offline mode
Book management
FR-2.x
Import, organize, search
Integrated viewer
FR-3.x
Reading, customization, profiles
Annotations and notes
FR-4.x
Highlights, notes, export
Progress tracking
FR-5.x
Statistics, sync
Goal management
FR-6.x
Goals, progress, notifications
Storage and sync
FR-7.x
Metadata server, file storage backends, OPDS
Non-functional requirements by category
Category
Scope
Description
Storage
NFR-1.x
File size, backends, encryption
Synchronization
NFR-2.x
Offline, conflicts, performance
Platform Support
NFR-3.x
Web, desktop, mobile, e-ink
Performance
NFR-4.x
Startup, open, search, scale
Usability
NFR-5.x
Design, accessibility, i18n
Security
NFR-6.x
Auth, encryption, privacy
Reliability
NFR-7.x
Uptime, integrity, backup
Extensibility
NFR-8.x
Plugin architecture
Maintainability
NFR-9.x
Code quality, logging
Dependency graphs
User management
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-1_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Create user<br align="left"/>account</b><br align="left"/><font point-size="10">UC-1_1</font><br align="left"/>>, tooltip="UC-1_1", href="../design/use-cases.html#UC-1_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-1_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Login with<br align="left"/>credentials</b><br align="left"/><font point-size="10">UC-1_2</font><br align="left"/>>, tooltip="UC-1_2", href="../design/use-cases.html#UC-1_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-1_3" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Login with<br align="left"/>Google account</b><br align="left"/><font point-size="10">UC-1_3</font><br align="left"/>>, tooltip="UC-1_3", href="../design/use-cases.html#UC-1_3", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-1_4" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Use application<br align="left"/>offline</b><br align="left"/><font point-size="10">UC-1_4</font><br align="left"/>>, tooltip="UC-1_4", href="../design/use-cases.html#UC-1_4", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-1_5" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Recover<br align="left"/>password</b><br align="left"/><font point-size="10">UC-1_5</font><br align="left"/>>, tooltip="UC-1_5", href="../design/use-cases.html#UC-1_5", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-1_6" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Delete account</b><br align="left"/><font point-size="10">UC-1_6</font><br align="left"/>>, tooltip="UC-1_6", href="../design/use-cases.html#UC-1_6", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-1_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Account<br align="left"/>registration</b><br align="left"/><font point-size="10">FR-1_1</font><br align="left"/>>, tooltip="FR-1_1", href="functional.html#FR-1_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-1_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Email/password<br align="left"/>login</b><br align="left"/><font point-size="10">FR-1_2</font><br align="left"/>>, tooltip="FR-1_2", href="functional.html#FR-1_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-1_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>OAuth login<br align="left"/>(Google)</b><br align="left"/><font point-size="10">FR-1_3</font><br align="left"/>>, tooltip="FR-1_3", href="functional.html#FR-1_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-1_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Offline mode</b><br align="left"/><font point-size="10">FR-1_4</font><br align="left"/>>, tooltip="FR-1_4", href="functional.html#FR-1_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-1_5" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Password<br align="left"/>recovery</b><br align="left"/><font point-size="10">FR-1_5</font><br align="left"/>>, tooltip="FR-1_5", href="functional.html#FR-1_5", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-1_6" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Account<br align="left"/>deletion</b><br align="left"/><font point-size="10">FR-1_6</font><br align="left"/>>, tooltip="FR-1_6", href="functional.html#FR-1_6", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-1_1" -> "FR-1_1" [arrowhead=vee];
"UC-1_2" -> "FR-1_2" [arrowhead=vee];
"UC-1_3" -> "FR-1_3" [arrowhead=vee];
"UC-1_4" -> "FR-1_4" [arrowhead=vee];
"UC-1_5" -> "FR-1_5" [arrowhead=vee];
"UC-1_6" -> "FR-1_6" [arrowhead=vee];
"FR-1_1" -> "UC-1_1" [arrowhead=vee];
"FR-1_2" -> "UC-1_2" [arrowhead=vee];
"FR-1_3" -> "UC-1_3" [arrowhead=vee];
"FR-1_4" -> "UC-1_4" [arrowhead=vee];
"FR-1_5" -> "UC-1_5" [arrowhead=vee];
"FR-1_6" -> "UC-1_6" [arrowhead=vee];
}
Book management
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-2_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Import book<br align="left"/>files</b><br align="left"/><font point-size="10">UC-2_1</font><br align="left"/>>, tooltip="UC-2_1", href="../design/use-cases.html#UC-2_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Convert book<br align="left"/>formats</b><br align="left"/><font point-size="10">UC-2_2</font><br align="left"/>>, tooltip="UC-2_2", href="../design/use-cases.html#UC-2_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_3" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Edit book<br align="left"/>metadata</b><br align="left"/><font point-size="10">UC-2_3</font><br align="left"/>>, tooltip="UC-2_3", href="../design/use-cases.html#UC-2_3", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_4" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Organize books<br align="left"/>into shelves</b><br align="left"/><font point-size="10">UC-2_4</font><br align="left"/>>, tooltip="UC-2_4", href="../design/use-cases.html#UC-2_4", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_5" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Tag books</b><br align="left"/><font point-size="10">UC-2_5</font><br align="left"/>>, tooltip="UC-2_5", href="../design/use-cases.html#UC-2_5", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_6" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Search books</b><br align="left"/><font point-size="10">UC-2_6</font><br align="left"/>>, tooltip="UC-2_6", href="../design/use-cases.html#UC-2_6", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_7" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Export books<br align="left"/>and data</b><br align="left"/><font point-size="10">UC-2_7</font><br align="left"/>>, tooltip="UC-2_7", href="../design/use-cases.html#UC-2_7", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_8" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Scan ISBN<br align="left"/>barcode</b><br align="left"/><font point-size="10">UC-2_8</font><br align="left"/>>, tooltip="UC-2_8", href="../design/use-cases.html#UC-2_8", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-2_9" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Track physical<br align="left"/>book</b><br align="left"/><font point-size="10">UC-2_9</font><br align="left"/>>, tooltip="UC-2_9", href="../design/use-cases.html#UC-2_9", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-2_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Format<br align="left"/>conversion</b><br align="left"/><font point-size="10">FR-2_1</font><br align="left"/>>, tooltip="FR-2_1", href="functional.html#FR-2_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Manual metadata<br align="left"/>editing</b><br align="left"/><font point-size="10">FR-2_2</font><br align="left"/>>, tooltip="FR-2_2", href="functional.html#FR-2_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Custom metadata<br align="left"/>fields</b><br align="left"/><font point-size="10">FR-2_3</font><br align="left"/>>, tooltip="FR-2_3", href="functional.html#FR-2_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Book file<br align="left"/>export</b><br align="left"/><font point-size="10">FR-2_4</font><br align="left"/>>, tooltip="FR-2_4", href="functional.html#FR-2_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_5" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Library data<br align="left"/>export</b><br align="left"/><font point-size="10">FR-2_5</font><br align="left"/>>, tooltip="FR-2_5", href="functional.html#FR-2_5", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_6" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Book import</b><br align="left"/><font point-size="10">FR-2_6</font><br align="left"/>>, tooltip="FR-2_6", href="functional.html#FR-2_6", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_7" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Full-text<br align="left"/>search</b><br align="left"/><font point-size="10">FR-2_7</font><br align="left"/>>, tooltip="FR-2_7", href="functional.html#FR-2_7", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_8" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Advanced search</b><br align="left"/><font point-size="10">FR-2_8</font><br align="left"/>>, tooltip="FR-2_8", href="functional.html#FR-2_8", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_9" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Shelf<br align="left"/>organization</b><br align="left"/><font point-size="10">FR-2_9</font><br align="left"/>>, tooltip="FR-2_9", href="functional.html#FR-2_9", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_10" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Book tagging</b><br align="left"/><font point-size="10">FR-2_10</font><br align="left"/>>, tooltip="FR-2_10", href="functional.html#FR-2_10", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_11" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Query language<br align="left"/>filters</b><br align="left"/><font point-size="10">FR-2_11</font><br align="left"/>>, tooltip="FR-2_11", href="functional.html#FR-2_11", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_12" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>ISBN barcode<br align="left"/>scanning</b><br align="left"/><font point-size="10">FR-2_12</font><br align="left"/>>, tooltip="FR-2_12", href="functional.html#FR-2_12", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_13" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Metadata<br align="left"/>fetching</b><br align="left"/><font point-size="10">FR-2_13</font><br align="left"/>>, tooltip="FR-2_13", href="functional.html#FR-2_13", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-2_14" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Physical book<br align="left"/>tracking</b><br align="left"/><font point-size="10">FR-2_14</font><br align="left"/>>, tooltip="FR-2_14", href="functional.html#FR-2_14", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-2_1" -> "FR-2_6" [arrowhead=vee];
"UC-2_2" -> "FR-2_1" [arrowhead=vee];
"UC-2_3" -> "FR-2_2" [arrowhead=vee];
"UC-2_3" -> "FR-2_13" [arrowhead=vee];
"UC-2_4" -> "FR-2_9" [arrowhead=vee];
"UC-2_5" -> "FR-2_10" [arrowhead=vee];
"UC-2_6" -> "FR-2_7" [arrowhead=vee];
"UC-2_6" -> "FR-2_8" [arrowhead=vee];
"UC-2_6" -> "FR-2_11" [arrowhead=vee];
"UC-2_7" -> "FR-2_4" [arrowhead=vee];
"UC-2_7" -> "FR-2_5" [arrowhead=vee];
"UC-2_8" -> "FR-2_12" [arrowhead=vee];
"UC-2_9" -> "FR-2_14" [arrowhead=vee];
"FR-2_1" -> "UC-2_2" [arrowhead=vee];
"FR-2_2" -> "UC-2_3" [arrowhead=vee];
"FR-2_3" -> "UC-2_3" [arrowhead=vee];
"FR-2_4" -> "UC-2_7" [arrowhead=vee];
"FR-2_5" -> "UC-2_7" [arrowhead=vee];
"FR-2_6" -> "UC-2_1" [arrowhead=vee];
"FR-2_7" -> "UC-2_6" [arrowhead=vee];
"FR-2_8" -> "UC-2_6" [arrowhead=vee];
"FR-2_9" -> "UC-2_4" [arrowhead=vee];
"FR-2_10" -> "UC-2_5" [arrowhead=vee];
"FR-2_11" -> "UC-2_6" [arrowhead=vee];
"FR-2_12" -> "UC-2_8" [arrowhead=vee];
"FR-2_13" -> "UC-2_3" [arrowhead=vee];
"FR-2_14" -> "UC-2_9" [arrowhead=vee];
}
Integrated viewer
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-3_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Read books with<br align="left"/>integrated<br align="left"/>viewer</b><br align="left"/><font point-size="10">UC-3_1</font><br align="left"/>>, tooltip="UC-3_1", href="../design/use-cases.html#UC-3_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-3_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Customize<br align="left"/>reading<br align="left"/>experience</b><br align="left"/><font point-size="10">UC-3_2</font><br align="left"/>>, tooltip="UC-3_2", href="../design/use-cases.html#UC-3_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-3_3" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Manage reading<br align="left"/>profiles</b><br align="left"/><font point-size="10">UC-3_3</font><br align="left"/>>, tooltip="UC-3_3", href="../design/use-cases.html#UC-3_3", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-3_4" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Manage<br align="left"/>bookmarks</b><br align="left"/><font point-size="10">UC-3_4</font><br align="left"/>>, tooltip="UC-3_4", href="../design/use-cases.html#UC-3_4", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-3_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>E-book reading</b><br align="left"/><font point-size="10">FR-3_1</font><br align="left"/>>, tooltip="FR-3_1", href="functional.html#FR-3_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-3_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Viewer<br align="left"/>customization</b><br align="left"/><font point-size="10">FR-3_2</font><br align="left"/>>, tooltip="FR-3_2", href="functional.html#FR-3_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-3_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Reading<br align="left"/>profiles</b><br align="left"/><font point-size="10">FR-3_3</font><br align="left"/>>, tooltip="FR-3_3", href="functional.html#FR-3_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-3_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Bookmarks</b><br align="left"/><font point-size="10">FR-3_4</font><br align="left"/>>, tooltip="FR-3_4", href="functional.html#FR-3_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-3_1" -> "FR-3_1" [arrowhead=vee];
"UC-3_2" -> "FR-3_2" [arrowhead=vee];
"UC-3_3" -> "FR-3_3" [arrowhead=vee];
"UC-3_4" -> "FR-3_4" [arrowhead=vee];
"FR-3_1" -> "UC-3_1" [arrowhead=vee];
"FR-3_2" -> "UC-3_2" [arrowhead=vee];
"FR-3_3" -> "UC-3_3" [arrowhead=vee];
"FR-3_4" -> "UC-3_4" [arrowhead=vee];
}
Annotations & notes
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-4_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Create text<br align="left"/>annotations</b><br align="left"/><font point-size="10">UC-4_1</font><br align="left"/>>, tooltip="UC-4_1", href="../design/use-cases.html#UC-4_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-4_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Create book<br align="left"/>notes</b><br align="left"/><font point-size="10">UC-4_2</font><br align="left"/>>, tooltip="UC-4_2", href="../design/use-cases.html#UC-4_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-4_3" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Manage<br align="left"/>annotations</b><br align="left"/><font point-size="10">UC-4_3</font><br align="left"/>>, tooltip="UC-4_3", href="../design/use-cases.html#UC-4_3", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-4_4" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Export<br align="left"/>annotations</b><br align="left"/><font point-size="10">UC-4_4</font><br align="left"/>>, tooltip="UC-4_4", href="../design/use-cases.html#UC-4_4", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-4_5" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Search<br align="left"/>annotations</b><br align="left"/><font point-size="10">UC-4_5</font><br align="left"/>>, tooltip="UC-4_5", href="../design/use-cases.html#UC-4_5", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-4_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Text<br align="left"/>highlighting</b><br align="left"/><font point-size="10">FR-4_1</font><br align="left"/>>, tooltip="FR-4_1", href="functional.html#FR-4_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-4_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Book notes</b><br align="left"/><font point-size="10">FR-4_2</font><br align="left"/>>, tooltip="FR-4_2", href="functional.html#FR-4_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-4_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Annotation<br align="left"/>editing</b><br align="left"/><font point-size="10">FR-4_3</font><br align="left"/>>, tooltip="FR-4_3", href="functional.html#FR-4_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-4_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Annotation<br align="left"/>export</b><br align="left"/><font point-size="10">FR-4_4</font><br align="left"/>>, tooltip="FR-4_4", href="functional.html#FR-4_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-4_5" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Annotation<br align="left"/>search</b><br align="left"/><font point-size="10">FR-4_5</font><br align="left"/>>, tooltip="FR-4_5", href="functional.html#FR-4_5", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-4_1" -> "FR-4_1" [arrowhead=vee];
"UC-4_2" -> "FR-4_2" [arrowhead=vee];
"UC-4_3" -> "FR-4_3" [arrowhead=vee];
"UC-4_4" -> "FR-4_4" [arrowhead=vee];
"UC-4_5" -> "FR-4_5" [arrowhead=vee];
"FR-4_1" -> "UC-4_1" [arrowhead=vee];
"FR-4_2" -> "UC-4_2" [arrowhead=vee];
"FR-4_3" -> "UC-4_3" [arrowhead=vee];
"FR-4_4" -> "UC-4_4" [arrowhead=vee];
"FR-4_5" -> "UC-4_5" [arrowhead=vee];
}
Progress & goals
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-5_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Track reading<br align="left"/>progress</b><br align="left"/><font point-size="10">UC-5_1</font><br align="left"/>>, tooltip="UC-5_1", href="../design/use-cases.html#UC-5_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-5_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Filter progress<br align="left"/>statistics</b><br align="left"/><font point-size="10">UC-5_2</font><br align="left"/>>, tooltip="UC-5_2", href="../design/use-cases.html#UC-5_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-6_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Create reading<br align="left"/>goals</b><br align="left"/><font point-size="10">UC-6_1</font><br align="left"/>>, tooltip="UC-6_1", href="../design/use-cases.html#UC-6_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-6_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Manage goal<br align="left"/>progress</b><br align="left"/><font point-size="10">UC-6_2</font><br align="left"/>>, tooltip="UC-6_2", href="../design/use-cases.html#UC-6_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-5_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Reading<br align="left"/>statistics</b><br align="left"/><font point-size="10">FR-5_1</font><br align="left"/>>, tooltip="FR-5_1", href="functional.html#FR-5_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-5_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Statistics<br align="left"/>filtering</b><br align="left"/><font point-size="10">FR-5_2</font><br align="left"/>>, tooltip="FR-5_2", href="functional.html#FR-5_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-5_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Cross-device<br align="left"/>sync</b><br align="left"/><font point-size="10">FR-5_3</font><br align="left"/>>, tooltip="FR-5_3", href="functional.html#FR-5_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-6_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Reading goals</b><br align="left"/><font point-size="10">FR-6_1</font><br align="left"/>>, tooltip="FR-6_1", href="functional.html#FR-6_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-6_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Manual goal<br align="left"/>updates</b><br align="left"/><font point-size="10">FR-6_2</font><br align="left"/>>, tooltip="FR-6_2", href="functional.html#FR-6_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-6_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Automatic goal<br align="left"/>progress</b><br align="left"/><font point-size="10">FR-6_3</font><br align="left"/>>, tooltip="FR-6_3", href="functional.html#FR-6_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-6_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Goal<br align="left"/>notifications</b><br align="left"/><font point-size="10">FR-6_4</font><br align="left"/>>, tooltip="FR-6_4", href="functional.html#FR-6_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-5_1" -> "FR-5_1" [arrowhead=vee];
"UC-5_2" -> "FR-5_2" [arrowhead=vee];
"UC-6_1" -> "FR-6_1" [arrowhead=vee];
"UC-6_2" -> "FR-6_2" [arrowhead=vee];
"UC-6_2" -> "FR-6_3" [arrowhead=vee];
"FR-5_1" -> "UC-5_1" [arrowhead=vee];
"FR-5_2" -> "UC-5_2" [arrowhead=vee];
"FR-6_1" -> "UC-6_1" [arrowhead=vee];
"FR-6_2" -> "UC-6_2" [arrowhead=vee];
"FR-6_3" -> "UC-6_2" [arrowhead=vee];
"FR-6_4" -> "UC-6_2" [arrowhead=vee];
}
Storage & sync
digraph needflow {
compound=true;
graph [
rankdir="LR";
bgcolor="transparent";
pad="0.3";
nodesep="0.4";
ranksep="0.6";
]
node [
fontname="system-ui, -apple-system, sans-serif";
fontsize="11";
fontcolor="#333333";
style="filled,rounded";
penwidth="1.2";
color="#cccccc";
]
edge [
color="#999999";
arrowsize="0.7";
penwidth="1.0";
fontname="system-ui, -apple-system, sans-serif";
fontsize="9";
fontcolor="#777777";
]
// node definitions
"UC-7_1" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Configure<br align="left"/>storage options</b><br align="left"/><font point-size="10">UC-7_1</font><br align="left"/>>, tooltip="UC-7_1", href="../design/use-cases.html#UC-7_1", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-7_2" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Process scanned<br align="left"/>documents</b><br align="left"/><font point-size="10">UC-7_2</font><br align="left"/>>, tooltip="UC-7_2", href="../design/use-cases.html#UC-7_2", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-7_3" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Synchronize<br align="left"/>data across<br align="left"/>devices</b><br align="left"/><font point-size="10">UC-7_3</font><br align="left"/>>, tooltip="UC-7_3", href="../design/use-cases.html#UC-7_3", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"UC-7_4" [label=<<font point-size="12">Use Case</font><br align="left"/><b>Browse OPDS<br align="left"/>catalog</b><br align="left"/><font point-size="10">UC-7_4</font><br align="left"/>>, tooltip="UC-7_4", href="../design/use-cases.html#UC-7_4", target="_top", shape="note", style="filled,rounded,filled", fillcolor="#6FA8D4"];
"FR-7_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>File storage<br align="left"/>backend<br align="left"/>selection</b><br align="left"/><font point-size="10">FR-7_1</font><br align="left"/>>, tooltip="FR-7_1", href="functional.html#FR-7_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-7_1_1" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Metadata server<br align="left"/>configuration</b><br align="left"/><font point-size="10">FR-7_1_1</font><br align="left"/>>, tooltip="FR-7_1_1", href="functional.html#FR-7_1_1", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-7_2" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>File upload</b><br align="left"/><font point-size="10">FR-7_2</font><br align="left"/>>, tooltip="FR-7_2", href="functional.html#FR-7_2", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-7_3" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>OCR processing</b><br align="left"/><font point-size="10">FR-7_3</font><br align="left"/>>, tooltip="FR-7_3", href="functional.html#FR-7_3", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-7_4" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>Plugin system</b><br align="left"/><font point-size="10">FR-7_4</font><br align="left"/>>, tooltip="FR-7_4", href="functional.html#FR-7_4", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
"FR-7_5" [label=<<font point-size="12">Functional Requirement</font><br align="left"/><b>OPDS catalog<br align="left"/>support</b><br align="left"/><font point-size="10">FR-7_5</font><br align="left"/>>, tooltip="FR-7_5", href="functional.html#FR-7_5", target="_top", shape="box3d", style="filled,rounded,filled", fillcolor="#7C6FD4"];
// edge definitions
"UC-7_1" -> "FR-7_1" [arrowhead=vee];
"UC-7_1" -> "FR-7_1_1" [arrowhead=vee];
"UC-7_2" -> "FR-7_3" [arrowhead=vee];
"UC-7_4" -> "FR-7_5" [arrowhead=vee];
"FR-7_1" -> "UC-7_1" [arrowhead=vee];
"FR-7_1_1" -> "UC-7_1" [arrowhead=vee];
"FR-7_3" -> "UC-7_2" [arrowhead=vee];
"FR-7_5" -> "UC-7_4" [arrowhead=vee];
}
Core principles
User data ownership
All user data, such as books, notes, annotations, can be exported in open formats, avoiding vendor lock-in and enabling self-hosting options for those who prefer full control over their data.
Cross-platform accessibility
Consistent experience and a full set of features across all devices and platforms, with additional platform-specific features, such as themes suitable for e-ink screens.
Offline functionality
No account or internet connection is required to use application’s core. Non-essential features, such as data synchronization and cloud file storage is optional.
Privacy first
Local-first architecture with no analytics or tracking enabled by default. A user can avoid relying on cloud services and use the system offline or with a self-hosted server instance.
Developer friendly
The system is fully open source and makes it easy to self-host the server for file storage and data sync. Documented REST API allows users to easily build their own clients and book management solutions.