-
Notifications
You must be signed in to change notification settings - Fork 1
Add offline book website with comprehensive Solana Protocol Design re… #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…search document This commit adds a standalone offline book website that displays the comprehensive research on Solana Protocol Design for Agent and MCP Server Registries. The implementation includes a single HTML file that loads markdown content directly from the same folder, with a responsive 90s ASCII grayscale theme design.
✅ Deploy Preview for aeamcp ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Reviewer's GuideThis PR transforms the static HTML report into a standalone, single-page offline book site: it dynamically loads markdown chapters with marked.js, applies a responsive 90s ASCII-grayscale monospace theme, introduces mobile-friendly navigation and PDF/print controls, and adds graceful loading, ASCII diagram support, and error messaging—all while removing legacy static content and Chart.js. Sequence Diagram for Dynamic Chapter LoadingsequenceDiagram
actor User
participant B as Browser (index.html + JS)
participant M as marked.js
participant FS as Local Filesystem
User->>B: Clicks chapter link (e.g., "Chapter 2")
B->>B: Calls loadChapter(2)
B->>User: Shows loading spinner in #markdown-content
B->>FS: fetch("research_draft_part2.md")
FS-->>B: Returns Markdown content for Chapter 2
B->>M: marked.parse(markdownContent)
M-->>B: Returns HTML content
B->>B: Updates #markdown-content with HTML
B->>B: Calls addNavigation(2) to update prev/next buttons
B->>B: Calls processAsciiDiagrams() to style <pre> tags
B->>User: Hides loading spinner
B->>User: Displays formatted Chapter 2 content
Class Diagram for index.html Structure and Key JavaScript FunctionsclassDiagram
class OfflineBookPage {
+String currentChapterId
+Map<String, String> chapterFiles
+HTMLElement header
+HTMLElement mainNav
+HTMLButtonElement mobileMenuButton
+HTMLElement bookSidebar
+HTMLButtonElement mobileSidebarButton
+HTMLElement chapterLinksContainer
+HTMLElement bookContentArea
+HTMLElement markdownDisplayArea
+HTMLElement navigationControlsArea
+HTMLElement footer
+loadChapter(chapterNum) void
+toggleMenu() void
+toggleSidebar() void
+addNavigation(currentChapter) void
+processAsciiDiagrams() void
+downloadPDF() void
+printPage() void
+handleKeydown(event) void
+handleResize() void
+initializeBook() void
}
OfflineBookPage "1" -- "1" HTMLElement : Renders In
OfflineBookPage -- "*" MarkdownFile : Loads
OfflineBookPage -- "1" MarkedJS : Uses for Parsing
class HTMLElement {
<<HTML Element>>
}
class HTMLButtonElement {
<<HTML Element>>
}
class MarkdownFile {
+String path
+String content
}
class MarkedJS {
<<External Library>>
+parse(markdownText) String
}
note for OfflineBookPage "Represents index.html and its associated JavaScript logic."
State Diagram for Chapter Content AreastateDiagram-v2
[*] --> Initializing
Initializing --> LoadingChapter : Load default chapter (Chapter 1)
LoadingChapter --> ChapterLoaded : Markdown fetched & parsed successfully
LoadingChapter --> ChapterLoadError : Error fetching or parsing markdown
ChapterLoaded --> LoadingChapter : User selects another chapter
ChapterLoadError --> LoadingChapter : User selects another chapter (retry)
ChapterLoaded : Displaying chapter content
ChapterLoadError : Displaying error message
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @0xrinegade - I've reviewed your changes and found some issues that need to be addressed.
Blocking issues:
- Using marked without sanitization can introduce XSS risks (link)
General comments:
- Extract the massive inline CSS and JavaScript from index.html into separate stylesheet and JS module files to make the page more maintainable and easier to navigate.
- Implement a simple caching layer for fetched markdown chapters so users don’t re-download the same file when navigating back and forth.
- Generate the table of contents and navigation links dynamically from your chapter list instead of hard-coding each link to reduce duplication and simplify future updates.
Here's what I looked at during the review
- 🟡 General issues: 15 issues found
- 🟢 Security: all looks good
- 🟢 Review instructions: all looks good
- 🟢 Testing: all looks good
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
index.html
Outdated
| - CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. --> | ||
| <title>Solana AI Registries: A Corporate Technical Overview (90s Monospace)</title> | ||
| <title>Solana Protocol Design for Agent and MCP Server Registries</title> | ||
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): Using marked without sanitization can introduce XSS risks
Sanitize the Markdown output (e.g., with DOMPurify or marked's sanitization) before inserting it into innerHTML to prevent XSS vulnerabilities.
index.html
Outdated
| const content = await response.text(); | ||
|
|
||
| // Render markdown content | ||
| document.getElementById('markdown-content').innerHTML = marked.parse(content); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): Direct innerHTML assignment of parsed Markdown is unsafe
Sanitize the output of marked.parse() before assigning it to innerHTML to prevent script injection.
| function processAsciiDiagrams() { | ||
| const preElements = document.querySelectorAll('pre'); | ||
| preElements.forEach(pre => { | ||
| // Check if this looks like an ASCII diagram | ||
| const content = pre.textContent; | ||
| if (content.includes('+---') || (content.includes('|') && content.includes('-'))) { | ||
| pre.classList.add('ascii-diagram'); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): ASCII diagram detection logic may misclassify code blocks
The current heuristic may incorrectly identify regular code as diagrams. Using a dedicated Markdown fence (e.g., ```ascii) or explicit metadata would improve accuracy.
| function processAsciiDiagrams() { | |
| const preElements = document.querySelectorAll('pre'); | |
| preElements.forEach(pre => { | |
| // Check if this looks like an ASCII diagram | |
| const content = pre.textContent; | |
| if (content.includes('+---') || (content.includes('|') && content.includes('-'))) { | |
| pre.classList.add('ascii-diagram'); | |
| } | |
| }); | |
| } | |
| function processAsciiDiagrams() { | |
| // Only select <pre><code class="language-ascii">...</code></pre> | |
| const asciiPreElements = document.querySelectorAll('pre > code.language-ascii'); | |
| asciiPreElements.forEach(code => { | |
| const pre = code.parentElement; | |
| if (pre) { | |
| pre.classList.add('ascii-diagram'); | |
| } | |
| }); | |
| } |
index.html
Outdated
| <h3>Error Loading Chapter</h3> | ||
| <p>Please ensure all markdown files are in the same directory as this HTML file.</p> | ||
| <p>Expected file: ${chapterFiles[chapterNum]}</p> | ||
| <p>Error details: ${error.message}</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): Exposing raw error.message to users may leak internals
Consider displaying a generic error to users and logging the detailed message elsewhere to avoid exposing internal information.
index.html
Outdated
| // Handle keyboard navigation | ||
| document.addEventListener('keydown', function(e) { | ||
| // Left arrow key for previous chapter | ||
| if (e.key === 'ArrowLeft' && currentChapter > 1) { | ||
| loadChapter(currentChapter - 1); | ||
| } | ||
| // Right arrow key for next chapter | ||
| else if (e.key === 'ArrowRight' && currentChapter < Object.keys(chapterFiles).length) { | ||
| loadChapter(currentChapter + 1); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Global arrow key handling may conflict with other components
Scoping the listener to only activate when the book container is focused or when not inside text inputs will help avoid interfering with default browser or input behaviors.
| // Handle keyboard navigation | |
| document.addEventListener('keydown', function(e) { | |
| // Left arrow key for previous chapter | |
| if (e.key === 'ArrowLeft' && currentChapter > 1) { | |
| loadChapter(currentChapter - 1); | |
| } | |
| // Right arrow key for next chapter | |
| else if (e.key === 'ArrowRight' && currentChapter < Object.keys(chapterFiles).length) { | |
| loadChapter(currentChapter + 1); | |
| } | |
| }); | |
| // Handle keyboard navigation, but only when not inside input, textarea, or contenteditable | |
| document.addEventListener('keydown', function(e) { | |
| const tag = e.target.tagName.toLowerCase(); | |
| const isEditable = e.target.isContentEditable; | |
| if ( | |
| tag === 'input' || | |
| tag === 'textarea' || | |
| isEditable | |
| ) { | |
| return; // Don't handle navigation if typing in an input, textarea, or contenteditable | |
| } | |
| // Optionally, scope to a specific container (uncomment and adjust if needed) | |
| // const bookContainer = document.getElementById('book-container'); | |
| // if (document.activeElement !== bookContainer) { | |
| // return; | |
| // } | |
| // Left arrow key for previous chapter | |
| if (e.key === 'ArrowLeft' && currentChapter > 1) { | |
| loadChapter(currentChapter - 1); | |
| } | |
| // Right arrow key for next chapter | |
| else if (e.key === 'ArrowRight' && currentChapter < Object.keys(chapterFiles).length) { | |
| loadChapter(currentChapter + 1); | |
| } | |
| }); |
| // Operational Parameters | ||
| pub rate_limit_requests: Option<u32>, | ||
| pub rate_limit_tokens: Option<u32>, | ||
| pub pricing_info_uri: Option<String>, // Max MAX_PRICING_URI_LEN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Missing #[max_len] for pricing_info_uri
Add the #[max_len(MAX_PRICING_URI_LEN)] annotation to ensure InitSpace allocates the correct size for pricing_info_uri.
| pub pricing_info_uri: Option<String>, // Max MAX_PRICING_URI_LEN | |
| #[max_len(MAX_PRICING_URI_LEN)] | |
| pub pricing_info_uri: Option<String>, // Max MAX_PRICING_URI_LEN |
| pub service_endpoints: Vec<ServiceEndpoint>, | ||
|
|
||
| // Off-chain Extension | ||
| pub extended_metadata_uri: Option<String>, // Max MAX_EXTENDED_URI_LEN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Missing #[max_len] for extended_metadata_uri in MCPServerRegistryEntryV1
Please add the #[max_len(MAX_EXTENDED_URI_LEN)] annotation to ensure correct sizing of extended_metadata_uri.
Suggested implementation:
// Off-chain Extension
#[max_len(MAX_EXTENDED_URI_LEN)]
pub extended_metadata_uri: Option<String>, // Max MAX_EXTENDED_URI_LEN
|
|
||
| **Example: Owner Index Traversal** | ||
|
|
||
| Let's consider an owner index for the Agent Registry, where index accounts map an owner's public key to the `agent_id`s they own: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (typo): Typo in 'agent_id`s'
'agent_ids' is used elsewhere in this section; update for consistency.
| Let's consider an owner index for the Agent Registry, where index accounts map an owner's public key to the `agent_id`s they own: | |
| Let's consider an owner index for the Agent Registry, where index accounts map an owner's public key to the `agent_ids` they own: |
| 3. **Denial of Service (DoS) via Large Data**: While `InitSpace` and `#[max_len]` help prevent excessively large accounts during initialization, update instructions need to validate the size of incoming variable-length data (`String`, `Vec`) to prevent attempts to store data exceeding reasonable limits or causing excessive computation during deserialization/validation. | ||
|
|
||
| 4. **Complex Enum Deserialization**: Borsh handles enums, but complex enum structures or changes in enum variants across program versions require careful handling during upgrades. | ||
|
|
||
| 5. **IDL Discrepancies**: If the program's IDL (used by clients) gets out of sync with the actual on-chain program's data structures, clients might serialize instruction data incorrectly, leading to deserialization failures. | ||
|
|
||
| **Mitigation Strategies:** | ||
|
|
||
| 1. **Use Anchor Types**: Rely on Anchor's `Account<T>`, `Program<T>`, `Signer`, etc., as they handle much of the underlying deserialization and validation securely. | ||
| 2. **Strict Validation**: Implement the input validation checks discussed previously (lengths, formats) to catch invalid data before or during deserialization. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: Clarify 'InitSpace' and '#[max_len]' terminology
These terms are not standard in Anchor or Rust. Please clarify if they are custom, or use standard Anchor syntax and terminology to prevent confusion.
| It's possible to generate program keypairs whose public keys start with a specific prefix (vanity address) using tools like `solana-keygen grind`. This can improve recognizability but offers no security benefit. | ||
| ```bash | ||
| # Example: Find a keypair whose public key starts with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: Incomplete example comment.
Please complete the example or remove the incomplete line if it's just a placeholder.
…execute buttons, expandable sidebar, markdown sanitization
…search document
This commit adds a standalone offline book website that displays the comprehensive research on Solana Protocol Design for Agent and MCP Server Registries. The implementation includes a single HTML file that loads markdown content directly from the same folder, with a responsive 90s ASCII grayscale theme design.
Summary by Sourcery
Transform the static report into a standalone offline book website with responsive 90s ASCII grayscale design, dynamic markdown content loading, and enhanced navigation features.
New Features:
Enhancements:
Documentation: