mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
docs: major cleanup and reorganization
- Archive 30 completed session documents to docs/archive/ - Extract learnings to docs/learnings/ (nix-flakes, nostr-sdk, grasp-audit) - Create CURRENT_STATUS.md as single source of truth - Create AGENTS.md with documentation guidelines - Create docs/archive/README.md for archive organization - Clean root directory: 32 files → 4 files Root directory now contains only: - README.md (project overview) - AGENTS.md (documentation guidelines) - CURRENT_STATUS.md (current state) - CLEANUP_SUMMARY.md (cleanup report) All historical documents preserved in docs/archive/ with proper dating. All reusable knowledge extracted to docs/learnings/. Benefits: - Easy to find current information - Clear document lifecycle - No more documentation sprawl - Learnings are accessible and reusable - Better onboarding for new developers/agents File counts: - Root: 4 (was 32) - Permanent docs: 7 - Learnings: 3 (new) - Archive: 32 (new) - Total: 49 well-organized docs
This commit is contained in:
@@ -0,0 +1,560 @@
|
||||
# AI Agent Guidelines for ngit-grasp
|
||||
|
||||
**Purpose:** Ensure AI agents (and humans) maintain consistent documentation practices and avoid common pitfalls.
|
||||
|
||||
**Last Updated:** November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## 📁 Documentation Structure
|
||||
|
||||
### Overview
|
||||
|
||||
We maintain a **clean, hierarchical documentation structure** to avoid documentation sprawl. All working documents have a defined lifecycle and location.
|
||||
|
||||
```
|
||||
ngit-grasp/
|
||||
├── README.md # Project overview (keep updated)
|
||||
├── AGENTS.md # This file - agent guidelines
|
||||
├── CHANGELOG.md # User-facing changes (semver)
|
||||
│
|
||||
├── docs/ # Permanent technical documentation
|
||||
│ ├── README.md # Docs navigation guide
|
||||
│ ├── ARCHITECTURE.md # System architecture
|
||||
│ ├── TEST_STRATEGY.md # Testing approach
|
||||
│ ├── GIT_PROTOCOL.md # Git protocol reference
|
||||
│ ├── COMPARISON.md # vs other implementations
|
||||
│ ├── GETTING_STARTED.md # Setup guide
|
||||
│ └── DECISION_SUMMARY.md # Key architectural decisions
|
||||
│
|
||||
├── docs/archive/ # Completed session/phase docs
|
||||
│ ├── 2025-11-04-tag-migration.md
|
||||
│ ├── 2025-11-04-flake-migration.md
|
||||
│ └── 2025-11-03-architecture-investigation.md
|
||||
│
|
||||
├── docs/learnings/ # Extracted knowledge (permanent)
|
||||
│ ├── nix-flakes.md # Flake gotchas and patterns
|
||||
│ ├── nostr-sdk.md # nostr-sdk patterns and upgrades
|
||||
│ └── git-http-backend.md # Git protocol learnings
|
||||
│
|
||||
├── grasp-audit/ # Audit tool subproject
|
||||
│ ├── README.md # Main audit docs
|
||||
│ ├── QUICK_START.md # Getting started
|
||||
│ └── docs/
|
||||
│ └── archive/ # Audit-specific archives
|
||||
│
|
||||
└── .ai/ # AI assistant context (ignored in git)
|
||||
└── history/ # Conversation history
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Document Lifecycle
|
||||
|
||||
### 1. Working Documents (Root Level)
|
||||
|
||||
**Purpose:** Active development, session notes, status reports
|
||||
**Location:** Project root
|
||||
**Lifecycle:** Created → Updated → Archived
|
||||
**Retention:** Archive after completion, delete if obsolete
|
||||
|
||||
**Examples:**
|
||||
- `TAG_MIGRATION_COMPLETE.md` → Archive when next phase starts
|
||||
- `SESSION_2025_11_04_SUMMARY.md` → Archive at session end
|
||||
- `NEXT_STEPS.md` → Update continuously, archive when complete
|
||||
|
||||
**Rules:**
|
||||
- ✅ Use descriptive names with dates: `YYYY-MM-DD-description.md`
|
||||
- ✅ Mark status clearly: `[WIP]`, `[COMPLETE]`, `[ARCHIVED]`
|
||||
- ✅ Include date and context at top
|
||||
- ❌ Don't let root accumulate more than 5-10 working docs
|
||||
- ❌ Don't create duplicates (merge or link instead)
|
||||
|
||||
### 2. Permanent Documentation (docs/)
|
||||
|
||||
**Purpose:** Long-term reference, architecture, guides
|
||||
**Location:** `docs/`
|
||||
**Lifecycle:** Created → Maintained → Updated
|
||||
**Retention:** Permanent (version controlled)
|
||||
|
||||
**Examples:**
|
||||
- `docs/ARCHITECTURE.md` - System design
|
||||
- `docs/TEST_STRATEGY.md` - Testing approach
|
||||
- `docs/learnings/nix-flakes.md` - Extracted knowledge
|
||||
|
||||
**Rules:**
|
||||
- ✅ Keep updated as project evolves
|
||||
- ✅ Use clear structure and headings
|
||||
- ✅ Link between related docs
|
||||
- ❌ Don't duplicate information (use links)
|
||||
- ❌ Don't include session-specific details
|
||||
|
||||
### 3. Archive (docs/archive/)
|
||||
|
||||
**Purpose:** Historical record, completed phases
|
||||
**Location:** `docs/archive/`
|
||||
**Lifecycle:** Moved from root → Archived
|
||||
**Retention:** Permanent (for reference)
|
||||
|
||||
**Examples:**
|
||||
- `docs/archive/2025-11-04-tag-migration.md`
|
||||
- `docs/archive/2025-11-03-architecture-investigation.md`
|
||||
|
||||
**Rules:**
|
||||
- ✅ Rename with date prefix when archiving
|
||||
- ✅ Add "ARCHIVED" marker at top
|
||||
- ✅ Extract learnings to docs/learnings/ first
|
||||
- ❌ Don't modify after archiving
|
||||
- ❌ Don't reference in active documentation
|
||||
|
||||
### 4. Learnings (docs/learnings/)
|
||||
|
||||
**Purpose:** Reusable knowledge, gotchas, patterns
|
||||
**Location:** `docs/learnings/`
|
||||
**Lifecycle:** Extracted → Maintained → Updated
|
||||
**Retention:** Permanent (living documents)
|
||||
|
||||
**Examples:**
|
||||
- `docs/learnings/nix-flakes.md` - Flake patterns and gotchas
|
||||
- `docs/learnings/nostr-sdk.md` - SDK upgrade notes
|
||||
- `docs/learnings/git-http-backend.md` - Git protocol tips
|
||||
|
||||
**Rules:**
|
||||
- ✅ Extract from session docs before archiving
|
||||
- ✅ Organize by topic, not by session
|
||||
- ✅ Include code examples
|
||||
- ✅ Update as we learn more
|
||||
- ❌ Don't duplicate official docs (link instead)
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Cleanup Process
|
||||
|
||||
### When to Clean Up
|
||||
|
||||
**Trigger:** Root directory has >10 markdown files
|
||||
**Frequency:** End of each major phase or weekly
|
||||
**Responsibility:** AI agents should proactively suggest cleanup
|
||||
|
||||
### Cleanup Steps
|
||||
|
||||
1. **Identify Completed Documents**
|
||||
```bash
|
||||
# Find old working docs
|
||||
ls -lt *.md | head -20
|
||||
```
|
||||
|
||||
2. **Extract Learnings**
|
||||
- Review each completed doc
|
||||
- Extract gotchas, patterns, solutions
|
||||
- Add to appropriate `docs/learnings/*.md`
|
||||
|
||||
3. **Archive Completed Work**
|
||||
```bash
|
||||
# Move to archive with date prefix
|
||||
mv TAG_MIGRATION_COMPLETE.md docs/archive/2025-11-04-tag-migration.md
|
||||
```
|
||||
|
||||
4. **Delete Obsolete Documents**
|
||||
- Duplicates (keep most recent/complete)
|
||||
- Superseded documents
|
||||
- Pure status reports (no learnings)
|
||||
|
||||
5. **Update References**
|
||||
- Update links in active docs
|
||||
- Update README.md if needed
|
||||
- Commit changes
|
||||
|
||||
### Example Cleanup
|
||||
|
||||
```bash
|
||||
# Before cleanup (36 files in root!)
|
||||
ls *.md | wc -l
|
||||
# 36
|
||||
|
||||
# After cleanup (5-8 files in root)
|
||||
ls *.md
|
||||
# README.md
|
||||
# AGENTS.md
|
||||
# CHANGELOG.md
|
||||
# CURRENT_STATUS.md
|
||||
# NEXT_STEPS.md
|
||||
|
||||
# Archived
|
||||
ls docs/archive/
|
||||
# 2025-11-04-tag-migration.md
|
||||
# 2025-11-04-flake-migration.md
|
||||
# 2025-11-03-architecture-investigation.md
|
||||
# ...
|
||||
|
||||
# Learnings extracted
|
||||
ls docs/learnings/
|
||||
# nix-flakes.md
|
||||
# nostr-sdk.md
|
||||
# git-http-backend.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Common Gotchas
|
||||
|
||||
### Nix Flakes
|
||||
|
||||
**Always use `nix develop`, not `nix-shell`**
|
||||
|
||||
```bash
|
||||
# ✅ Correct
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
nix develop -c cargo build
|
||||
|
||||
# ❌ Wrong
|
||||
nix-shell
|
||||
nix-shell --run "cargo build"
|
||||
```
|
||||
|
||||
**Why:** We use `flake.nix`, not `shell.nix`. See `docs/learnings/nix-flakes.md`.
|
||||
|
||||
**Flake Commands:**
|
||||
```bash
|
||||
# Show flake outputs
|
||||
nix flake show
|
||||
|
||||
# Update flake inputs
|
||||
nix flake update
|
||||
|
||||
# Build package
|
||||
nix build
|
||||
|
||||
# Run package
|
||||
nix run
|
||||
```
|
||||
|
||||
### Git Subprojects
|
||||
|
||||
**grasp-audit is a subproject with its own flake**
|
||||
|
||||
```bash
|
||||
# ✅ Correct - enter grasp-audit environment
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
cargo build
|
||||
|
||||
# ❌ Wrong - can't build from root
|
||||
cd ngit-grasp
|
||||
cargo build # This won't find grasp-audit
|
||||
```
|
||||
|
||||
**Why:** `grasp-audit/` has its own `Cargo.toml` and `flake.nix`.
|
||||
|
||||
### nostr-sdk Versions
|
||||
|
||||
**We use nostr-sdk 0.43.x (latest stable)**
|
||||
|
||||
```toml
|
||||
# ✅ Correct
|
||||
[dependencies]
|
||||
nostr-sdk = "0.43"
|
||||
|
||||
# ❌ Wrong
|
||||
nostr-sdk = "0.35" # Old version, breaking changes
|
||||
```
|
||||
|
||||
**Why:** We upgraded from 0.35 to 0.43. See `docs/learnings/nostr-sdk.md` for migration notes.
|
||||
|
||||
**Common Breaking Changes:**
|
||||
- `EventBuilder::new()` signature changed
|
||||
- Tag API changed to `Tag::custom()`
|
||||
- Filter API changed
|
||||
- See archived upgrade docs for details
|
||||
|
||||
### Testing Patterns
|
||||
|
||||
**Integration tests require relay**
|
||||
|
||||
```bash
|
||||
# ✅ Correct - start relay first
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
|
||||
# Then run tests
|
||||
cd grasp-audit
|
||||
nix develop -c cargo test --ignored
|
||||
|
||||
# ❌ Wrong - integration tests will fail
|
||||
cargo test --ignored # No relay running
|
||||
```
|
||||
|
||||
**Test Organization:**
|
||||
```rust
|
||||
// Unit tests (no relay needed)
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_something() { }
|
||||
}
|
||||
|
||||
// Integration tests (relay required)
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
#[ignore] // Requires relay
|
||||
fn test_against_relay() { }
|
||||
}
|
||||
```
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
**Keep README.md synchronized**
|
||||
|
||||
When you:
|
||||
- Complete a major feature → Update README.md status
|
||||
- Change architecture → Update docs/ARCHITECTURE.md
|
||||
- Add dependencies → Update README.md tech stack
|
||||
- Change workflow → Update docs/GETTING_STARTED.md
|
||||
|
||||
**Don't:**
|
||||
- Create duplicate documentation
|
||||
- Leave stale status markers
|
||||
- Forget to update CHANGELOG.md for user-facing changes
|
||||
|
||||
---
|
||||
|
||||
## 📝 Writing Guidelines
|
||||
|
||||
### Markdown Style
|
||||
|
||||
```markdown
|
||||
# Title (H1 - only one per file)
|
||||
|
||||
**Date:** YYYY-MM-DD
|
||||
**Status:** [WIP|COMPLETE|ARCHIVED]
|
||||
|
||||
## Section (H2)
|
||||
|
||||
### Subsection (H3)
|
||||
|
||||
**Bold** for emphasis, `code` for commands/code.
|
||||
|
||||
- Bullet lists for items
|
||||
- Keep consistent style
|
||||
|
||||
1. Numbered lists for sequences
|
||||
2. Use when order matters
|
||||
|
||||
✅ Use emoji for status (sparingly)
|
||||
❌ Don't overuse emoji
|
||||
|
||||
\`\`\`bash
|
||||
# Code blocks with language
|
||||
cargo build
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### Status Markers
|
||||
|
||||
- `[WIP]` - Work in progress
|
||||
- `[COMPLETE]` - Finished, may be archived
|
||||
- `[ARCHIVED]` - Moved to archive, historical only
|
||||
- `✅` - Success/complete
|
||||
- `❌` - Failure/incorrect
|
||||
- `⏳` - In progress
|
||||
- `🔜` - Planned/next
|
||||
|
||||
### Document Headers
|
||||
|
||||
```markdown
|
||||
# Document Title
|
||||
|
||||
**Purpose:** One-line purpose
|
||||
**Date:** YYYY-MM-DD
|
||||
**Status:** [WIP|COMPLETE|ARCHIVED]
|
||||
**Related:** Links to related docs
|
||||
|
||||
---
|
||||
|
||||
## Content starts here
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤖 AI Agent Responsibilities
|
||||
|
||||
### Before Creating New Documents
|
||||
|
||||
1. **Check if document already exists**
|
||||
```bash
|
||||
find . -name "*keyword*.md"
|
||||
```
|
||||
|
||||
2. **Check if information can be added to existing doc**
|
||||
- Prefer updating over creating
|
||||
- Use sections/subsections
|
||||
|
||||
3. **Determine correct location**
|
||||
- Working doc → Root
|
||||
- Permanent → docs/
|
||||
- Learning → docs/learnings/
|
||||
- Historical → docs/archive/
|
||||
|
||||
4. **Use descriptive names with dates**
|
||||
- `YYYY-MM-DD-description.md` for working docs
|
||||
- `topic-name.md` for permanent docs
|
||||
|
||||
### During Development
|
||||
|
||||
1. **Update status markers**
|
||||
- Mark WIP → COMPLETE when done
|
||||
- Update README.md status section
|
||||
|
||||
2. **Extract learnings as you go**
|
||||
- Add gotchas to docs/learnings/
|
||||
- Don't wait until cleanup
|
||||
|
||||
3. **Keep documentation DRY**
|
||||
- Link to existing docs
|
||||
- Don't duplicate information
|
||||
|
||||
### End of Session
|
||||
|
||||
1. **Suggest cleanup if needed**
|
||||
- Count root .md files
|
||||
- Suggest archiving completed docs
|
||||
|
||||
2. **Create session summary**
|
||||
- What was accomplished
|
||||
- What's next
|
||||
- Any blockers
|
||||
|
||||
3. **Update permanent docs**
|
||||
- Sync README.md with reality
|
||||
- Update relevant docs/ files
|
||||
|
||||
### Cleanup Time
|
||||
|
||||
1. **Review all root .md files**
|
||||
2. **Extract learnings to docs/learnings/**
|
||||
3. **Archive completed work to docs/archive/**
|
||||
4. **Delete obsolete duplicates**
|
||||
5. **Update links in active docs**
|
||||
6. **Commit with clear message**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Quality Checklist
|
||||
|
||||
### For Every Document
|
||||
|
||||
- [ ] Clear purpose stated at top
|
||||
- [ ] Date included
|
||||
- [ ] Status marker present
|
||||
- [ ] Proper heading hierarchy (H1 → H2 → H3)
|
||||
- [ ] Code blocks have language specified
|
||||
- [ ] Links are valid and relative
|
||||
- [ ] No duplicate information
|
||||
- [ ] Spell-checked and readable
|
||||
|
||||
### For Working Documents
|
||||
|
||||
- [ ] Descriptive filename with date
|
||||
- [ ] Will be archived or deleted when done
|
||||
- [ ] Not duplicating permanent docs
|
||||
- [ ] Learnings extracted to docs/learnings/
|
||||
|
||||
### For Permanent Documents
|
||||
|
||||
- [ ] In correct docs/ subdirectory
|
||||
- [ ] Linked from docs/README.md
|
||||
- [ ] Updated as project evolves
|
||||
- [ ] No session-specific details
|
||||
- [ ] Serves long-term purpose
|
||||
|
||||
### For Archived Documents
|
||||
|
||||
- [ ] Moved to docs/archive/
|
||||
- [ ] Renamed with date prefix
|
||||
- [ ] ARCHIVED marker at top
|
||||
- [ ] Learnings extracted first
|
||||
- [ ] Not referenced in active docs
|
||||
|
||||
---
|
||||
|
||||
## 📚 Reference Documents
|
||||
|
||||
### Must Read
|
||||
- **This file (AGENTS.md)** - Guidelines for documentation
|
||||
- **README.md** - Project overview
|
||||
- **docs/README.md** - Documentation navigation
|
||||
|
||||
### Key Technical Docs
|
||||
- **docs/ARCHITECTURE.md** - System design
|
||||
- **docs/TEST_STRATEGY.md** - Testing approach
|
||||
- **docs/GETTING_STARTED.md** - Setup guide
|
||||
|
||||
### Learnings (Gotchas)
|
||||
- **docs/learnings/nix-flakes.md** - Nix flake patterns
|
||||
- **docs/learnings/nostr-sdk.md** - nostr-sdk notes
|
||||
- **docs/learnings/git-http-backend.md** - Git protocol tips
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Quick Links
|
||||
|
||||
- [Project README](README.md)
|
||||
- [Documentation Index](docs/README.md)
|
||||
- [Architecture](docs/ARCHITECTURE.md)
|
||||
- [Learnings](docs/learnings/)
|
||||
- [Archive](docs/archive/)
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips for Success
|
||||
|
||||
1. **Less is more** - Prefer updating over creating
|
||||
2. **Archive often** - Keep root clean
|
||||
3. **Extract learnings** - Make knowledge reusable
|
||||
4. **Link, don't duplicate** - DRY applies to docs too
|
||||
5. **Date everything** - Context is important
|
||||
6. **Use descriptive names** - Future you will thank you
|
||||
7. **Check before creating** - Document might already exist
|
||||
8. **Update as you go** - Don't wait for cleanup time
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
After reading this:
|
||||
|
||||
1. **Review current documentation structure**
|
||||
```bash
|
||||
ls -la *.md
|
||||
ls -la docs/
|
||||
```
|
||||
|
||||
2. **Identify cleanup candidates**
|
||||
- Completed working docs
|
||||
- Obsolete duplicates
|
||||
- Session summaries
|
||||
|
||||
3. **Extract learnings**
|
||||
- Review completed docs
|
||||
- Add to docs/learnings/
|
||||
|
||||
4. **Archive and clean**
|
||||
- Move to docs/archive/
|
||||
- Delete obsolete files
|
||||
- Update links
|
||||
|
||||
5. **Commit changes**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "docs: cleanup and reorganization"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Remember:** Good documentation structure is like good code structure - it makes everything easier.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: November 4, 2025*
|
||||
*Status: ✅ Active guidelines*
|
||||
@@ -0,0 +1,448 @@
|
||||
# Documentation Cleanup - November 4, 2025
|
||||
|
||||
**Purpose:** Summary of documentation reorganization
|
||||
**Status:** ✅ Complete
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Cleaned up **32 markdown files** from project root, organizing them into a clear, maintainable structure.
|
||||
|
||||
**Before:** 32 files in root (documentation sprawl)
|
||||
**After:** 3 files in root (clean structure)
|
||||
|
||||
---
|
||||
|
||||
## What Changed
|
||||
|
||||
### Root Directory
|
||||
|
||||
**Before:**
|
||||
```
|
||||
32 markdown files including:
|
||||
- Session summaries
|
||||
- Status reports
|
||||
- Migration docs
|
||||
- Implementation reports
|
||||
- Quick references
|
||||
- Planning documents
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
3 essential files:
|
||||
- README.md # Project overview
|
||||
- AGENTS.md # AI agent guidelines
|
||||
- CURRENT_STATUS.md # Current project state
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### New Structure
|
||||
|
||||
```
|
||||
docs/
|
||||
├── README.md # Docs navigation
|
||||
├── ARCHITECTURE.md # System design
|
||||
├── TEST_STRATEGY.md # Testing approach
|
||||
├── GETTING_STARTED.md # Setup guide
|
||||
├── GIT_PROTOCOL.md # Git protocol reference
|
||||
├── COMPARISON.md # vs other implementations
|
||||
├── DECISION_SUMMARY.md # Key decisions
|
||||
│
|
||||
├── learnings/ # Reusable knowledge
|
||||
│ ├── nix-flakes.md # Nix patterns & gotchas ✨ NEW
|
||||
│ ├── nostr-sdk.md # nostr-sdk 0.43 notes ✨ NEW
|
||||
│ └── grasp-audit.md # Audit tool patterns ✨ NEW
|
||||
│
|
||||
└── archive/ # Historical documents
|
||||
├── README.md # Archive index ✨ NEW
|
||||
├── 2025-11-03-*.md # Nov 3 session docs (16 files)
|
||||
└── 2025-11-04-*.md # Nov 4 session docs (14 files)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documents Archived
|
||||
|
||||
### November 3, 2025 (16 files)
|
||||
|
||||
**Investigation & Planning:**
|
||||
- architecture-investigation.md
|
||||
- review-summary.md
|
||||
- documentation-index.md
|
||||
- grasp-audit-plan.md
|
||||
|
||||
**Implementation:**
|
||||
- grasp-audit-implementation.md
|
||||
- implementation-complete.md
|
||||
- verification-complete.md
|
||||
|
||||
**Testing:**
|
||||
- compliance-test-proposal.md
|
||||
- compliance-testing-report.md
|
||||
- test-breakdown.md
|
||||
- smoke-test-report.md
|
||||
- final-audit-report.md
|
||||
- final-summary.md
|
||||
|
||||
**Reference:**
|
||||
- files-created.md
|
||||
- quick-reference.md
|
||||
- start-here.md
|
||||
|
||||
---
|
||||
|
||||
### November 4, 2025 (14 files)
|
||||
|
||||
**Migrations:**
|
||||
- tag-migration.md
|
||||
- tag-migration-summary.md
|
||||
- flake-migration.md
|
||||
|
||||
**Upgrades:**
|
||||
- nostr-sdk-upgrade.md
|
||||
- upgrade-complete.md
|
||||
|
||||
**Fixes:**
|
||||
- compilation-fixes.md
|
||||
- audit-system-fixed.md
|
||||
- audit-status-report.md
|
||||
|
||||
**Sessions:**
|
||||
- session-summary.md
|
||||
- session-complete-1.md
|
||||
- session-complete-2.md
|
||||
- session-continuation.md
|
||||
|
||||
**Planning:**
|
||||
- next-session-quickstart.md
|
||||
- next-prompt.md
|
||||
- ready-for-next-phase.md
|
||||
|
||||
---
|
||||
|
||||
## Learnings Extracted
|
||||
|
||||
Created 3 new learning documents with reusable knowledge:
|
||||
|
||||
### 1. docs/learnings/nix-flakes.md
|
||||
|
||||
**Content:**
|
||||
- Critical gotcha: Use `nix develop`, not `nix-shell`
|
||||
- Flake structure and patterns
|
||||
- Common commands
|
||||
- Subproject flakes
|
||||
- Migration from shell.nix
|
||||
- Benefits and best practices
|
||||
- Common issues and solutions
|
||||
|
||||
**Extracted from:**
|
||||
- FLAKE_MIGRATION_COMPLETE.md
|
||||
- Various session documents
|
||||
- Real experience during development
|
||||
|
||||
---
|
||||
|
||||
### 2. docs/learnings/nostr-sdk.md
|
||||
|
||||
**Content:**
|
||||
- Current version: 0.43.x
|
||||
- Breaking changes from 0.35 → 0.43
|
||||
- Common patterns (events, tags, queries)
|
||||
- Testing patterns (unit vs integration)
|
||||
- Common gotchas and solutions
|
||||
- Performance tips
|
||||
- Migration checklist
|
||||
|
||||
**Extracted from:**
|
||||
- NOSTR_SDK_0.43_UPGRADE.md
|
||||
- Implementation experience
|
||||
- Test code examples
|
||||
|
||||
---
|
||||
|
||||
### 3. docs/learnings/grasp-audit.md
|
||||
|
||||
**Content:**
|
||||
- Architecture decisions
|
||||
- Audit event tagging strategy
|
||||
- Code patterns
|
||||
- Test isolation
|
||||
- Cleanup strategy
|
||||
- Testing organization
|
||||
- Lessons learned
|
||||
- Common issues
|
||||
|
||||
**Extracted from:**
|
||||
- TAG_MIGRATION_COMPLETE.md
|
||||
- GRASP_AUDIT_PLAN.md
|
||||
- Implementation summaries
|
||||
- Testing experience
|
||||
|
||||
---
|
||||
|
||||
## New Documents Created
|
||||
|
||||
### CURRENT_STATUS.md
|
||||
|
||||
**Purpose:** Single source of truth for project state
|
||||
|
||||
**Content:**
|
||||
- Quick summary
|
||||
- Project structure
|
||||
- What works
|
||||
- What's next
|
||||
- Development workflow
|
||||
- Key technologies
|
||||
- Important gotchas
|
||||
- Recent milestones
|
||||
- Success metrics
|
||||
- Resources
|
||||
|
||||
**Replaces:** Multiple status reports and session summaries
|
||||
|
||||
---
|
||||
|
||||
### AGENTS.md (Updated)
|
||||
|
||||
**Purpose:** AI agent documentation guidelines
|
||||
|
||||
**Already existed but now enforced:**
|
||||
- Documentation structure
|
||||
- Document lifecycle
|
||||
- Cleanup process
|
||||
- Common gotchas
|
||||
- Writing guidelines
|
||||
- AI agent responsibilities
|
||||
- Quality checklist
|
||||
|
||||
---
|
||||
|
||||
### docs/archive/README.md
|
||||
|
||||
**Purpose:** Archive organization and usage guide
|
||||
|
||||
**Content:**
|
||||
- Archive organization
|
||||
- Document index by date/topic
|
||||
- When to reference archives
|
||||
- Extracting learnings
|
||||
- Archive principles
|
||||
- Quick find by topic/date
|
||||
|
||||
---
|
||||
|
||||
## Benefits Achieved
|
||||
|
||||
### 1. Clarity
|
||||
|
||||
✅ **Easy to find current information**
|
||||
- `CURRENT_STATUS.md` - where we are
|
||||
- `README.md` - what the project is
|
||||
- `AGENTS.md` - how to document
|
||||
|
||||
✅ **Easy to find historical information**
|
||||
- `docs/archive/` - organized by date
|
||||
- `docs/archive/README.md` - searchable index
|
||||
|
||||
---
|
||||
|
||||
### 2. Maintainability
|
||||
|
||||
✅ **Clear document lifecycle**
|
||||
- Working docs in root
|
||||
- Permanent docs in docs/
|
||||
- Learnings extracted
|
||||
- Completed work archived
|
||||
|
||||
✅ **No more sprawl**
|
||||
- Root directory stays clean
|
||||
- Archive grows but stays organized
|
||||
- Learnings get updated, not duplicated
|
||||
|
||||
---
|
||||
|
||||
### 3. Reusability
|
||||
|
||||
✅ **Learnings are accessible**
|
||||
- Organized by topic, not session
|
||||
- Include code examples
|
||||
- Link to historical context
|
||||
- Living documents that evolve
|
||||
|
||||
✅ **Patterns are documented**
|
||||
- Nix flake patterns
|
||||
- nostr-sdk patterns
|
||||
- grasp-audit patterns
|
||||
- Testing patterns
|
||||
|
||||
---
|
||||
|
||||
### 4. Onboarding
|
||||
|
||||
✅ **New developers (human or AI) can:**
|
||||
1. Read `README.md` - understand project
|
||||
2. Read `CURRENT_STATUS.md` - know where we are
|
||||
3. Read `AGENTS.md` - learn documentation practices
|
||||
4. Read `docs/learnings/` - avoid known pitfalls
|
||||
5. Reference `docs/archive/` - understand history
|
||||
|
||||
---
|
||||
|
||||
## Cleanup Statistics
|
||||
|
||||
### Before
|
||||
|
||||
```
|
||||
Root directory:
|
||||
- 32 markdown files
|
||||
- Mix of status, reports, plans, summaries
|
||||
- Hard to find current information
|
||||
- Duplicate information
|
||||
- No clear organization
|
||||
|
||||
docs/ directory:
|
||||
- 7 permanent docs
|
||||
- 0 learnings
|
||||
- 0 archived docs
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```
|
||||
Root directory:
|
||||
- 3 markdown files (README, AGENTS, CURRENT_STATUS)
|
||||
- Clean and focused
|
||||
- Clear purpose for each file
|
||||
|
||||
docs/ directory:
|
||||
- 7 permanent docs (unchanged)
|
||||
- 3 learnings (NEW)
|
||||
- 30 archived docs (NEW)
|
||||
- 1 archive index (NEW)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Document Count
|
||||
|
||||
| Location | Count | Purpose |
|
||||
|----------|-------|---------|
|
||||
| Root | 3 | Essential project files |
|
||||
| docs/ | 7 | Permanent documentation |
|
||||
| docs/learnings/ | 3 | Reusable knowledge |
|
||||
| docs/archive/ | 30 | Historical records |
|
||||
| **Total** | **43** | **Well-organized docs** |
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Going Forward
|
||||
|
||||
### Daily Development
|
||||
|
||||
**Create working docs in root:**
|
||||
- Session notes
|
||||
- Status updates
|
||||
- Temporary planning
|
||||
|
||||
**Keep root clean:**
|
||||
- Max 5-10 working docs
|
||||
- Archive when complete
|
||||
- Extract learnings first
|
||||
|
||||
---
|
||||
|
||||
### Weekly Cleanup
|
||||
|
||||
**Trigger:** Root has >10 markdown files
|
||||
|
||||
**Process:**
|
||||
1. Review completed working docs
|
||||
2. Extract learnings to `docs/learnings/`
|
||||
3. Archive to `docs/archive/YYYY-MM-DD-topic.md`
|
||||
4. Delete obsolete duplicates
|
||||
5. Update `CURRENT_STATUS.md`
|
||||
6. Commit changes
|
||||
|
||||
---
|
||||
|
||||
### Guidelines
|
||||
|
||||
**Follow `AGENTS.md` for:**
|
||||
- When to create new documents
|
||||
- Where to put documents
|
||||
- How to name documents
|
||||
- When to archive
|
||||
- How to extract learnings
|
||||
|
||||
---
|
||||
|
||||
## Commit Message
|
||||
|
||||
```
|
||||
docs: major cleanup and reorganization
|
||||
|
||||
- Archive 30 completed session documents to docs/archive/
|
||||
- Extract learnings to docs/learnings/ (nix-flakes, nostr-sdk, grasp-audit)
|
||||
- Create CURRENT_STATUS.md as single source of truth
|
||||
- Create docs/archive/README.md for archive organization
|
||||
- Clean root directory: 32 files → 3 files
|
||||
- Enforce AGENTS.md documentation guidelines
|
||||
|
||||
Root directory now contains only:
|
||||
- README.md (project overview)
|
||||
- AGENTS.md (documentation guidelines)
|
||||
- CURRENT_STATUS.md (current state)
|
||||
|
||||
All historical documents preserved in docs/archive/ with proper dating.
|
||||
All reusable knowledge extracted to docs/learnings/.
|
||||
|
||||
Benefits:
|
||||
- Easy to find current information
|
||||
- Clear document lifecycle
|
||||
- No more documentation sprawl
|
||||
- Learnings are accessible and reusable
|
||||
- Better onboarding for new developers/agents
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Verify structure
|
||||
ls -la *.md
|
||||
# Should show: README.md, AGENTS.md, CURRENT_STATUS.md
|
||||
|
||||
ls -la docs/learnings/
|
||||
# Should show: nix-flakes.md, nostr-sdk.md, grasp-audit.md
|
||||
|
||||
ls -la docs/archive/ | wc -l
|
||||
# Should show: 31 (30 files + README.md)
|
||||
|
||||
# Verify no broken links (manual check)
|
||||
grep -r "\.md" docs/ | grep -v ".git"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Cleanup complete
|
||||
2. ✅ Learnings extracted
|
||||
3. ✅ Archive organized
|
||||
4. 🔜 Commit changes
|
||||
5. 🔜 Start NIP-01 relay implementation
|
||||
|
||||
---
|
||||
|
||||
**Cleanup completed:** November 4, 2025
|
||||
**Files organized:** 43 total
|
||||
**Root cleaned:** 32 → 3 files
|
||||
**Status:** ✅ Ready for next phase
|
||||
|
||||
---
|
||||
|
||||
*This document will be archived after commit*
|
||||
@@ -0,0 +1,464 @@
|
||||
# ngit-grasp - Current Status
|
||||
|
||||
**Date:** November 4, 2025
|
||||
**Phase:** Audit Tool Complete - Ready for NIP-01 Implementation
|
||||
**Status:** 🟢 All Systems Green
|
||||
|
||||
---
|
||||
|
||||
## Quick Summary
|
||||
|
||||
✅ **grasp-audit tool complete** - NIP-01 smoke tests passing
|
||||
✅ **Tag migration complete** - Using standard NIP-01 "t" tags
|
||||
✅ **nostr-sdk upgraded** - Version 0.43.x (latest stable)
|
||||
✅ **Nix flakes migrated** - Modern reproducible builds
|
||||
✅ **Documentation cleaned** - Clear structure established
|
||||
|
||||
**Next:** Build NIP-01 relay implementation, test with grasp-audit
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
ngit-grasp/
|
||||
├── README.md # Project overview
|
||||
├── AGENTS.md # AI agent guidelines
|
||||
├── CURRENT_STATUS.md # This file
|
||||
│
|
||||
├── docs/ # Permanent documentation
|
||||
│ ├── ARCHITECTURE.md # System design
|
||||
│ ├── TEST_STRATEGY.md # Testing approach
|
||||
│ ├── GETTING_STARTED.md # Setup guide
|
||||
│ ├── GIT_PROTOCOL.md # Git protocol reference
|
||||
│ ├── COMPARISON.md # vs ngit-relay
|
||||
│ ├── DECISION_SUMMARY.md # Key decisions
|
||||
│ │
|
||||
│ ├── learnings/ # Reusable knowledge
|
||||
│ │ ├── nix-flakes.md # Nix flake patterns
|
||||
│ │ ├── nostr-sdk.md # nostr-sdk 0.43 notes
|
||||
│ │ └── grasp-audit.md # Audit tool patterns
|
||||
│ │
|
||||
│ └── archive/ # Historical documents
|
||||
│ ├── 2025-11-04-tag-migration.md
|
||||
│ ├── 2025-11-04-flake-migration.md
|
||||
│ ├── 2025-11-04-nostr-sdk-upgrade.md
|
||||
│ └── ...
|
||||
│
|
||||
└── grasp-audit/ # Audit tool (separate crate)
|
||||
├── README.md # Audit tool docs
|
||||
├── QUICK_START.md # Getting started
|
||||
├── flake.nix # Nix dev environment
|
||||
├── Cargo.toml # Rust dependencies
|
||||
└── src/
|
||||
├── specs/ # Test specifications
|
||||
│ └── nip01_smoke.rs # NIP-01 basic tests ✅
|
||||
├── audit.rs # Audit config & event builder
|
||||
├── client.rs # Audit client wrapper
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Works
|
||||
|
||||
### grasp-audit Tool ✅
|
||||
|
||||
**Status:** Fully functional, all tests passing
|
||||
|
||||
```bash
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
cargo test --lib # 12/12 unit tests ✅
|
||||
cargo test -- --ignored # 1/1 integration test ✅
|
||||
cargo run -- audit --relay ws://localhost:7000 --spec nip01-smoke
|
||||
# Results: 6/6 passed (100.0%) ✅
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ NIP-01 smoke tests (websocket, events, subscriptions)
|
||||
- ✅ CI and production modes
|
||||
- ✅ Test isolation via unique run IDs
|
||||
- ✅ Standard "t" tag usage
|
||||
- ✅ Audit event cleanup strategy
|
||||
- ✅ CLI interface
|
||||
|
||||
**Test Coverage:**
|
||||
- websocket_connection
|
||||
- send_receive_event
|
||||
- create_subscription
|
||||
- close_subscription
|
||||
- reject_invalid_signature
|
||||
- reject_invalid_event_id
|
||||
|
||||
---
|
||||
|
||||
### Development Environment ✅
|
||||
|
||||
**Nix Flakes:**
|
||||
- ✅ `grasp-audit/flake.nix` - Reproducible builds
|
||||
- ✅ Rust toolchain via rust-overlay
|
||||
- ✅ All dependencies managed
|
||||
- ✅ Cross-platform support
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
cd grasp-audit
|
||||
nix develop # Enter dev shell
|
||||
nix develop -c cargo build # One-off command
|
||||
nix build # Build package
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Documentation ✅
|
||||
|
||||
**Permanent Docs:**
|
||||
- ✅ `docs/ARCHITECTURE.md` - Detailed system design
|
||||
- ✅ `docs/TEST_STRATEGY.md` - Testing approach
|
||||
- ✅ `docs/GETTING_STARTED.md` - Setup guide
|
||||
- ✅ `docs/README.md` - Documentation index
|
||||
|
||||
**Learnings:**
|
||||
- ✅ `docs/learnings/nix-flakes.md` - Nix patterns and gotchas
|
||||
- ✅ `docs/learnings/nostr-sdk.md` - nostr-sdk 0.43 migration
|
||||
- ✅ `docs/learnings/grasp-audit.md` - Audit tool patterns
|
||||
|
||||
**Guidelines:**
|
||||
- ✅ `AGENTS.md` - AI agent documentation practices
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
### Immediate: NIP-01 Relay Implementation
|
||||
|
||||
**Goal:** Build basic Nostr relay that passes grasp-audit tests
|
||||
|
||||
**Approach:**
|
||||
1. Create `src/` directory structure
|
||||
2. Implement basic NIP-01 relay using nostr-relay-builder
|
||||
3. Run grasp-audit tests against it
|
||||
4. Iterate until all tests pass
|
||||
|
||||
**Files to Create:**
|
||||
```
|
||||
src/
|
||||
├── main.rs # Entry point
|
||||
├── config.rs # Configuration
|
||||
├── nostr/
|
||||
│ ├── mod.rs
|
||||
│ ├── relay.rs # NIP-01 relay setup
|
||||
│ └── events.rs # Event handling
|
||||
└── storage/
|
||||
├── mod.rs
|
||||
└── repository.rs # Event storage
|
||||
```
|
||||
|
||||
**Success Criteria:**
|
||||
```bash
|
||||
# Start ngit-grasp relay
|
||||
cargo run
|
||||
|
||||
# In another terminal
|
||||
cd grasp-audit
|
||||
cargo run -- audit --relay ws://localhost:8080 --spec nip01-smoke
|
||||
# Results: 6/6 passed (100.0%) ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: GRASP-01 Compliance
|
||||
|
||||
**After NIP-01 works:**
|
||||
|
||||
1. **Extend grasp-audit**
|
||||
- Create `src/specs/grasp_01_relay.rs`
|
||||
- Test repository announcements (NIP-34)
|
||||
- Test state events
|
||||
- Test maintainer validation
|
||||
|
||||
2. **Implement in ngit-grasp**
|
||||
- NIP-34 event validation
|
||||
- Repository state management
|
||||
- Maintainer authorization
|
||||
|
||||
3. **Iterate**
|
||||
- Run GRASP-01 audit tests
|
||||
- Fix failures
|
||||
- Repeat until passing
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Git Integration
|
||||
|
||||
**After GRASP-01 compliance:**
|
||||
|
||||
1. **Git HTTP Backend**
|
||||
- Implement git-smart-http handlers
|
||||
- Integrate with authorization
|
||||
|
||||
2. **Push Validation**
|
||||
- Query Nostr state events
|
||||
- Validate push permissions
|
||||
- Inline authorization (no hooks)
|
||||
|
||||
3. **Full GRASP-01**
|
||||
- Complete service requirements
|
||||
- End-to-end testing
|
||||
|
||||
---
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Daily Development
|
||||
|
||||
```bash
|
||||
# For ngit-grasp (when we create it)
|
||||
cd ngit-grasp
|
||||
nix develop
|
||||
cargo build
|
||||
cargo test
|
||||
cargo run
|
||||
|
||||
# For grasp-audit
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
cargo build
|
||||
cargo test --lib
|
||||
cargo test -- --ignored # Requires relay
|
||||
cargo run -- audit --relay ws://localhost:8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Running Tests
|
||||
|
||||
**Unit Tests (Fast):**
|
||||
```bash
|
||||
# grasp-audit
|
||||
cd grasp-audit
|
||||
cargo test --lib
|
||||
|
||||
# ngit-grasp (when created)
|
||||
cargo test --lib
|
||||
```
|
||||
|
||||
**Integration Tests (Requires Relay):**
|
||||
```bash
|
||||
# Start test relay
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
|
||||
# Run integration tests
|
||||
cd grasp-audit
|
||||
cargo test -- --ignored
|
||||
```
|
||||
|
||||
**Audit Tests:**
|
||||
```bash
|
||||
# Start your relay
|
||||
cd ngit-grasp
|
||||
cargo run
|
||||
|
||||
# Run audit in another terminal
|
||||
cd grasp-audit
|
||||
cargo run -- audit --relay ws://localhost:8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Technologies
|
||||
|
||||
### Current Stack
|
||||
|
||||
- **Rust**: Core language
|
||||
- **nostr-sdk 0.43**: Nostr event handling
|
||||
- **Nix Flakes**: Reproducible dev environment
|
||||
- **Cargo**: Build system
|
||||
- **Docker**: Test relay (nostr-rs-relay)
|
||||
|
||||
### Planned Stack (ngit-grasp)
|
||||
|
||||
- **actix-web**: HTTP server
|
||||
- **nostr-relay-builder**: Relay infrastructure
|
||||
- **git-http-backend**: Git protocol handling
|
||||
- **tokio**: Async runtime
|
||||
|
||||
---
|
||||
|
||||
## Important Gotchas
|
||||
|
||||
### 1. Use Nix Flakes, Not nix-shell
|
||||
|
||||
```bash
|
||||
# ✅ Correct
|
||||
nix develop
|
||||
|
||||
# ❌ Wrong
|
||||
nix-shell
|
||||
```
|
||||
|
||||
**Why:** We use `flake.nix`, not `shell.nix`
|
||||
|
||||
---
|
||||
|
||||
### 2. grasp-audit is Separate
|
||||
|
||||
```bash
|
||||
# ✅ Correct
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
cargo build
|
||||
|
||||
# ❌ Wrong
|
||||
cd ngit-grasp
|
||||
cargo build # Won't find grasp-audit
|
||||
```
|
||||
|
||||
**Why:** Separate crate with own flake and Cargo.toml
|
||||
|
||||
---
|
||||
|
||||
### 3. Integration Tests Need Relay
|
||||
|
||||
```bash
|
||||
# ✅ Correct
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
cargo test -- --ignored
|
||||
|
||||
# ❌ Wrong
|
||||
cargo test -- --ignored # Will fail without relay
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. nostr-sdk 0.43 API Changes
|
||||
|
||||
**Event Building:**
|
||||
```rust
|
||||
// ✅ Correct (0.43)
|
||||
EventBuilder::new(kind, content)
|
||||
.tags(tags)
|
||||
.sign_with_keys(&keys)?
|
||||
|
||||
// ❌ Wrong (0.35)
|
||||
EventBuilder::new(kind, content, tags)
|
||||
.to_event(&keys)?
|
||||
```
|
||||
|
||||
**See:** `docs/learnings/nostr-sdk.md` for full migration guide
|
||||
|
||||
---
|
||||
|
||||
## Documentation Practices
|
||||
|
||||
### When to Create Documents
|
||||
|
||||
**Working Docs (Root):**
|
||||
- Session summaries
|
||||
- Status reports
|
||||
- Next steps
|
||||
- Temporary notes
|
||||
|
||||
**Permanent Docs (docs/):**
|
||||
- Architecture
|
||||
- Design decisions
|
||||
- API documentation
|
||||
- User guides
|
||||
|
||||
**Learnings (docs/learnings/):**
|
||||
- Gotchas and patterns
|
||||
- Migration notes
|
||||
- Best practices
|
||||
- Reusable knowledge
|
||||
|
||||
**Archive (docs/archive/):**
|
||||
- Completed session docs
|
||||
- Historical records
|
||||
- Superseded documents
|
||||
|
||||
**See:** `AGENTS.md` for full guidelines
|
||||
|
||||
---
|
||||
|
||||
## Recent Milestones
|
||||
|
||||
- ✅ **Nov 4, 2025** - Tag migration to standard "t" tags
|
||||
- ✅ **Nov 4, 2025** - Flake migration (shell.nix → flake.nix)
|
||||
- ✅ **Nov 4, 2025** - nostr-sdk upgrade (0.35 → 0.43)
|
||||
- ✅ **Nov 4, 2025** - Documentation cleanup
|
||||
- ✅ **Nov 3, 2025** - Architecture investigation complete
|
||||
- ✅ **Nov 3, 2025** - grasp-audit tool implemented
|
||||
- ✅ **Nov 3, 2025** - NIP-01 smoke tests passing
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Current Status
|
||||
|
||||
| Metric | Status | Details |
|
||||
|--------|--------|---------|
|
||||
| grasp-audit builds | ✅ | Clean build, no warnings |
|
||||
| Unit tests | ✅ | 12/12 passing |
|
||||
| Integration tests | ✅ | 1/1 passing |
|
||||
| CLI works | ✅ | All commands functional |
|
||||
| Smoke tests | ✅ | 6/6 passing |
|
||||
| Documentation | ✅ | Complete and organized |
|
||||
| Nix flakes | ✅ | Reproducible builds |
|
||||
|
||||
### Next Milestone: NIP-01 Relay
|
||||
|
||||
| Metric | Status | Target |
|
||||
|--------|--------|--------|
|
||||
| ngit-grasp builds | 🔜 | Clean build |
|
||||
| NIP-01 relay running | 🔜 | Accepts connections |
|
||||
| Smoke tests pass | 🔜 | 6/6 against ngit-grasp |
|
||||
| Basic event storage | 🔜 | Events persist |
|
||||
| Subscriptions work | 🔜 | Real-time updates |
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- [Project README](README.md)
|
||||
- [Architecture](docs/ARCHITECTURE.md)
|
||||
- [Test Strategy](docs/TEST_STRATEGY.md)
|
||||
- [Getting Started](docs/GETTING_STARTED.md)
|
||||
- [Agent Guidelines](AGENTS.md)
|
||||
|
||||
### Learnings
|
||||
- [Nix Flakes](docs/learnings/nix-flakes.md)
|
||||
- [nostr-sdk](docs/learnings/nostr-sdk.md)
|
||||
- [grasp-audit](docs/learnings/grasp-audit.md)
|
||||
|
||||
### External
|
||||
- [GRASP Protocol](https://gitworkshop.dev/danconwaydev.com/grasp)
|
||||
- [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md)
|
||||
- [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md)
|
||||
- [nostr-sdk docs](https://docs.rs/nostr-sdk/0.43.0)
|
||||
|
||||
---
|
||||
|
||||
## Contact & Contribution
|
||||
|
||||
**Status:** Alpha - Active Development
|
||||
**License:** MIT
|
||||
**Repository:** ngit-grasp (local development)
|
||||
|
||||
**Contributing:**
|
||||
1. Read `AGENTS.md` for documentation practices
|
||||
2. Review `docs/ARCHITECTURE.md` for design
|
||||
3. Check `CURRENT_STATUS.md` (this file) for current state
|
||||
4. Follow Rust conventions (`cargo fmt`, `cargo clippy`)
|
||||
5. Add tests for new functionality
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** November 4, 2025
|
||||
**Next Review:** When NIP-01 relay is implemented
|
||||
|
||||
---
|
||||
|
||||
*Status: 🟢 Ready to build NIP-01 relay implementation*
|
||||
@@ -0,0 +1,158 @@
|
||||
# Archive - Historical Documentation
|
||||
|
||||
**Purpose:** Completed session documents, phase reports, and historical records
|
||||
**Status:** Read-only - documents are not modified after archiving
|
||||
|
||||
---
|
||||
|
||||
## Archive Organization
|
||||
|
||||
Documents are organized by date (YYYY-MM-DD) and topic.
|
||||
|
||||
### November 3, 2025 - Architecture Investigation & Initial Implementation
|
||||
|
||||
**Architecture Investigation:**
|
||||
- `2025-11-03-architecture-investigation.md` - GRASP protocol investigation complete
|
||||
- `2025-11-03-review-summary.md` - Executive summary of investigation
|
||||
- `2025-11-03-documentation-index.md` - Initial docs structure
|
||||
|
||||
**grasp-audit Implementation:**
|
||||
- `2025-11-03-grasp-audit-plan.md` - Audit tool design decisions
|
||||
- `2025-11-03-grasp-audit-implementation.md` - Implementation summary
|
||||
- `2025-11-03-implementation-complete.md` - Initial implementation complete
|
||||
- `2025-11-03-verification-complete.md` - Verification results
|
||||
|
||||
**Testing:**
|
||||
- `2025-11-03-compliance-test-proposal.md` - Test strategy proposal
|
||||
- `2025-11-03-compliance-testing-report.md` - Compliance testing report
|
||||
- `2025-11-03-test-breakdown.md` - Detailed test breakdown
|
||||
- `2025-11-03-smoke-test-report.md` - Smoke test results
|
||||
- `2025-11-03-final-audit-report.md` - Final audit report
|
||||
- `2025-11-03-final-summary.md` - Final summary
|
||||
|
||||
**Reference:**
|
||||
- `2025-11-03-files-created.md` - Files created during investigation
|
||||
- `2025-11-03-quick-reference.md` - Quick reference guide
|
||||
- `2025-11-03-start-here.md` - Getting started guide
|
||||
|
||||
---
|
||||
|
||||
### November 4, 2025 - Upgrades & Migrations
|
||||
|
||||
**Tag Migration:**
|
||||
- `2025-11-04-tag-migration.md` - Migration to standard "t" tags (detailed)
|
||||
- `2025-11-04-tag-migration-summary.md` - Migration summary
|
||||
|
||||
**Flake Migration:**
|
||||
- `2025-11-04-flake-migration.md` - shell.nix → flake.nix migration
|
||||
|
||||
**nostr-sdk Upgrade:**
|
||||
- `2025-11-04-nostr-sdk-upgrade.md` - 0.35 → 0.43 upgrade guide
|
||||
- `2025-11-04-upgrade-complete.md` - Upgrade completion report
|
||||
|
||||
**Fixes & Improvements:**
|
||||
- `2025-11-04-compilation-fixes.md` - Compilation fixes
|
||||
- `2025-11-04-audit-system-fixed.md` - Audit system fixes
|
||||
- `2025-11-04-audit-status-report.md` - Audit status report
|
||||
|
||||
**Session Summaries:**
|
||||
- `2025-11-04-session-summary.md` - Main session summary
|
||||
- `2025-11-04-session-complete-1.md` - Session completion 1
|
||||
- `2025-11-04-session-complete-2.md` - Session completion 2
|
||||
- `2025-11-04-session-continuation.md` - Session continuation
|
||||
|
||||
**Planning:**
|
||||
- `2025-11-04-next-session-quickstart.md` - Next session quickstart
|
||||
- `2025-11-04-next-prompt.md` - Next prompt planning
|
||||
- `2025-11-04-ready-for-next-phase.md` - Phase readiness report
|
||||
|
||||
---
|
||||
|
||||
## Using Archived Documents
|
||||
|
||||
### When to Reference
|
||||
|
||||
✅ **Good reasons to reference:**
|
||||
- Understanding historical context
|
||||
- Learning from past decisions
|
||||
- Reviewing what was tried before
|
||||
- Tracking project evolution
|
||||
|
||||
❌ **Don't reference for:**
|
||||
- Current implementation details (use `docs/` instead)
|
||||
- Active development (use `CURRENT_STATUS.md`)
|
||||
- Reusable patterns (use `docs/learnings/`)
|
||||
|
||||
### Extracting Learnings
|
||||
|
||||
If you find useful patterns or gotchas in archived documents:
|
||||
|
||||
1. Extract to appropriate `docs/learnings/*.md` file
|
||||
2. Update with current context
|
||||
3. Link to archive for historical context
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
<!-- In docs/learnings/nostr-sdk.md -->
|
||||
|
||||
## Tag Migration Pattern
|
||||
|
||||
When changing tag structure...
|
||||
|
||||
**Reference:** See `docs/archive/2025-11-04-tag-migration.md` for detailed migration story.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Archive Principles
|
||||
|
||||
1. **Immutable**: Documents are not modified after archiving
|
||||
2. **Dated**: All filenames include YYYY-MM-DD prefix
|
||||
3. **Organized**: Grouped by date and topic
|
||||
4. **Referenced**: Can be linked from active docs for context
|
||||
5. **Searchable**: Full-text search helps find historical info
|
||||
|
||||
---
|
||||
|
||||
## Document Lifecycle
|
||||
|
||||
```
|
||||
Working Doc (root)
|
||||
↓
|
||||
Extract Learnings → docs/learnings/
|
||||
↓
|
||||
Archive → docs/archive/
|
||||
↓
|
||||
Reference (read-only)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Find
|
||||
|
||||
### By Topic
|
||||
|
||||
- **Architecture**: `2025-11-03-architecture-investigation.md`
|
||||
- **Testing**: `2025-11-03-*-test-*.md`
|
||||
- **Migrations**: `2025-11-04-*-migration.md`
|
||||
- **Upgrades**: `2025-11-04-*-upgrade.md`
|
||||
- **Sessions**: `2025-11-04-session-*.md`
|
||||
|
||||
### By Date
|
||||
|
||||
- **Nov 3**: Initial investigation and implementation
|
||||
- **Nov 4**: Upgrades, migrations, and refinements
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **Active Status**: `../CURRENT_STATUS.md`
|
||||
- **Learnings**: `../learnings/`
|
||||
- **Architecture**: `../ARCHITECTURE.md`
|
||||
- **Guidelines**: `../../AGENTS.md`
|
||||
|
||||
---
|
||||
|
||||
*Archive established: November 4, 2025*
|
||||
*Total documents: 30*
|
||||
@@ -0,0 +1,498 @@
|
||||
# GRASP Audit Tool - Patterns and Learnings
|
||||
|
||||
**Purpose:** Document grasp-audit architecture, patterns, and lessons learned
|
||||
**Last Updated:** November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
`grasp-audit` is a compliance testing tool for GRASP (Git Relays Authorized via Signed-Nostr Proofs) protocol implementations. It tests both Nostr relay compliance (NIP-01) and GRASP-specific functionality.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### Separate Crate Strategy
|
||||
|
||||
**Decision:** Build `grasp-audit` as a separate crate from `ngit-grasp`
|
||||
|
||||
**Why:**
|
||||
1. **Parallel Development**: Can build tests before implementation
|
||||
2. **Isolated Testing**: Tests run in isolation (CI/CD safe)
|
||||
3. **Production Auditing**: Can audit live production services
|
||||
4. **Reusability**: Other GRASP implementations can use it
|
||||
|
||||
**Location:** `grasp-audit/` subdirectory with own `Cargo.toml` and `flake.nix`
|
||||
|
||||
---
|
||||
|
||||
### Audit Event Tagging Strategy
|
||||
|
||||
**Problem:** Test events pollute the relay and need cleanup without deletion events.
|
||||
|
||||
**Solution:** Use special tags to mark audit events:
|
||||
|
||||
```rust
|
||||
// Every audit event includes these tags
|
||||
[
|
||||
["t", "grasp-audit-test-event"], // Marker
|
||||
["t", "audit-{run-id}"], // Run isolation
|
||||
["t", "audit-cleanup-after-{timestamp}"] // Cleanup time
|
||||
]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ **Queryable**: Can find all audit events via tag filter
|
||||
- ✅ **Isolated**: Each test run has unique run ID
|
||||
- ✅ **Self-cleaning**: Cleanup timestamp indicates when to delete
|
||||
- ✅ **No deletion events**: Direct database cleanup, no KIND 5 events
|
||||
- ✅ **Production safe**: Won't interfere with real events
|
||||
|
||||
**Reference:** See `docs/archive/2025-11-04-tag-migration.md`
|
||||
|
||||
---
|
||||
|
||||
### Standard "t" Tags vs Custom Tags
|
||||
|
||||
**Evolution:**
|
||||
1. **Original**: Custom single-letter tags (`g`, `r`, `c`)
|
||||
2. **Current**: Standard NIP-01 "t" tags with prefixed values
|
||||
|
||||
**Why we changed:**
|
||||
- ❌ Custom tags could conflict with other systems
|
||||
- ✅ "t" tag is standard for categorization/topics
|
||||
- ✅ Multiple "t" tags are expected and supported
|
||||
- ✅ Self-documenting values (`audit-{run-id}` vs just `{run-id}`)
|
||||
- ✅ Better namespacing with prefixes
|
||||
|
||||
**Migration:** Completed November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## Code Patterns
|
||||
|
||||
### Audit Configuration
|
||||
|
||||
```rust
|
||||
use grasp_audit::audit::AuditConfig;
|
||||
|
||||
// CI mode - isolated test runs
|
||||
let config = AuditConfig::ci();
|
||||
// Generates UUID run ID: "ci-{uuid}"
|
||||
// Cleanup after 1 hour
|
||||
|
||||
// Production mode - persistent run ID
|
||||
let config = AuditConfig::production("prod-server-1");
|
||||
// Uses provided run ID
|
||||
// Cleanup after 24 hours
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- **CI mode**: Automated testing, parallel runs, temporary
|
||||
- **Production mode**: Manual audits, monitoring, persistent
|
||||
|
||||
---
|
||||
|
||||
### Creating Audit Events
|
||||
|
||||
```rust
|
||||
use grasp_audit::audit::{AuditConfig, AuditEventBuilder};
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
let config = AuditConfig::ci();
|
||||
let keys = Keys::generate();
|
||||
|
||||
// Create audit event
|
||||
let event = AuditEventBuilder::new(&config, Kind::TextNote, "test content")
|
||||
.build(&keys)?;
|
||||
|
||||
// Event automatically includes:
|
||||
// - Audit marker tag
|
||||
// - Run ID tag
|
||||
// - Cleanup timestamp tag
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Querying Audit Events
|
||||
|
||||
```rust
|
||||
use grasp_audit::client::AuditClient;
|
||||
use grasp_audit::audit::AuditConfig;
|
||||
|
||||
let config = AuditConfig::ci();
|
||||
let client = AuditClient::new(config, keys);
|
||||
|
||||
// Connect to relay
|
||||
client.add_relay("ws://localhost:7000").await?;
|
||||
client.connect().await;
|
||||
|
||||
// Query audit events for this run
|
||||
let events = client.query().await?;
|
||||
|
||||
// Events are filtered by:
|
||||
// - "grasp-audit-test-event" marker
|
||||
// - Current run ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Isolation
|
||||
|
||||
**Each test run is isolated by unique run ID:**
|
||||
|
||||
```rust
|
||||
// CI mode generates unique UUID per run
|
||||
let config1 = AuditConfig::ci();
|
||||
let config2 = AuditConfig::ci();
|
||||
|
||||
// config1.run_id != config2.run_id
|
||||
// Tests won't interfere with each other
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Parallel CI/CD runs don't conflict
|
||||
- ✅ Can run multiple test suites simultaneously
|
||||
- ✅ Easy to identify which run created which events
|
||||
- ✅ Cleanup can target specific runs
|
||||
|
||||
---
|
||||
|
||||
### Cleanup Strategy
|
||||
|
||||
**Two-phase cleanup:**
|
||||
|
||||
1. **Automatic expiry** via cleanup timestamp tag
|
||||
2. **Manual cleanup** by querying and deleting
|
||||
|
||||
```rust
|
||||
// Events include cleanup timestamp
|
||||
["t", "audit-cleanup-after-1730707200"]
|
||||
|
||||
// Cleanup process:
|
||||
// 1. Query events with expired cleanup timestamp
|
||||
// 2. Delete from database directly (no KIND 5)
|
||||
// 3. Avoid deletion event pollution
|
||||
```
|
||||
|
||||
**Implementation:** To be built in relay (not in audit tool)
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Test Organization
|
||||
|
||||
```
|
||||
grasp-audit/src/specs/
|
||||
├── nip01_smoke.rs # NIP-01 basic functionality
|
||||
├── grasp_01_relay.rs # GRASP-01 relay requirements (planned)
|
||||
└── mod.rs # Test suite registry
|
||||
```
|
||||
|
||||
### Unit vs Integration Tests
|
||||
|
||||
**Unit Tests** (no relay required):
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_audit_config() {
|
||||
let config = AuditConfig::ci();
|
||||
assert!(config.run_id.starts_with("ci-"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration Tests** (relay required):
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[tokio::test]
|
||||
#[ignore] // Requires relay
|
||||
async fn test_smoke_tests_against_relay() {
|
||||
// Test against real relay
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Running tests:**
|
||||
```bash
|
||||
# Unit tests (fast, no dependencies)
|
||||
cargo test --lib
|
||||
|
||||
# Integration tests (requires relay)
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
cargo test -- --ignored
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Result Reporting
|
||||
|
||||
```rust
|
||||
use grasp_audit::result::AuditResult;
|
||||
|
||||
// Run tests
|
||||
let results = vec![
|
||||
AuditResult::pass("websocket_connection", "Connected successfully"),
|
||||
AuditResult::fail("invalid_event", "Expected rejection, got acceptance"),
|
||||
];
|
||||
|
||||
// Report
|
||||
for result in &results {
|
||||
println!("{}", result);
|
||||
}
|
||||
|
||||
// Summary
|
||||
let passed = results.iter().filter(|r| r.is_pass()).count();
|
||||
let total = results.len();
|
||||
println!("Results: {}/{} passed ({:.1}%)",
|
||||
passed, total, (passed as f64 / total as f64) * 100.0);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Design
|
||||
|
||||
### Command Structure
|
||||
|
||||
```bash
|
||||
grasp-audit audit [OPTIONS]
|
||||
|
||||
Options:
|
||||
--relay <URL> Relay to test (required)
|
||||
--mode <MODE> ci or production (default: ci)
|
||||
--run-id <ID> Custom run ID (production mode only)
|
||||
--spec <SPEC> Test spec to run (default: all)
|
||||
--verbose Detailed output
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```bash
|
||||
# CI mode - quick smoke test
|
||||
grasp-audit audit \
|
||||
--relay ws://localhost:7000 \
|
||||
--mode ci \
|
||||
--spec nip01-smoke
|
||||
|
||||
# Production mode - full compliance audit
|
||||
grasp-audit audit \
|
||||
--relay wss://relay.example.com \
|
||||
--mode production \
|
||||
--run-id "audit-2025-11-04" \
|
||||
--verbose
|
||||
|
||||
# Test all specs
|
||||
grasp-audit audit --relay ws://localhost:7000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
### 1. Tag Migration is Breaking
|
||||
|
||||
**Lesson:** Changing tag structure breaks event queries.
|
||||
|
||||
**Impact:** Events created with old tags won't be found by new queries.
|
||||
|
||||
**Mitigation:**
|
||||
- ✅ Accept breaking changes in alpha stage
|
||||
- ✅ Document migration clearly
|
||||
- ✅ Old events auto-expire via cleanup
|
||||
- ✅ No production deployments affected
|
||||
|
||||
**Reference:** `docs/archive/2025-11-04-tag-migration.md`
|
||||
|
||||
---
|
||||
|
||||
### 2. Test Data Lifecycle Matters
|
||||
|
||||
**Lesson:** Test events accumulate and pollute relay.
|
||||
|
||||
**Solution:** Built-in cleanup strategy from day one.
|
||||
|
||||
**Implementation:**
|
||||
- Every event has cleanup timestamp
|
||||
- Relay can cleanup expired events
|
||||
- No deletion event pollution (direct DB cleanup)
|
||||
|
||||
---
|
||||
|
||||
### 3. Isolation Enables Parallel Testing
|
||||
|
||||
**Lesson:** Unique run IDs enable parallel test execution.
|
||||
|
||||
**Benefit:** CI/CD can run multiple test suites simultaneously.
|
||||
|
||||
**Pattern:**
|
||||
```rust
|
||||
// Each CI run gets unique ID
|
||||
let config = AuditConfig::ci();
|
||||
// run_id = "ci-{uuid}"
|
||||
|
||||
// Tests isolated by run ID
|
||||
let events = client.query().await?;
|
||||
// Only returns events for this run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Standards Compliance Reduces Friction
|
||||
|
||||
**Lesson:** Using standard NIP-01 "t" tags instead of custom tags.
|
||||
|
||||
**Benefits:**
|
||||
- ✅ No conflicts with other systems
|
||||
- ✅ Standard relay filtering works
|
||||
- ✅ Better interoperability
|
||||
- ✅ Self-documenting
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
|
||||
- [ ] **GRASP-01 Test Suite**: Repository announcement and state event tests
|
||||
- [ ] **Test Report Generation**: JSON/HTML output for CI/CD
|
||||
- [ ] **Performance Benchmarks**: Measure relay performance
|
||||
- [ ] **Relay Comparison**: Side-by-side compliance comparison
|
||||
- [ ] **Continuous Monitoring**: Periodic production audits
|
||||
|
||||
---
|
||||
|
||||
### Possible Improvements
|
||||
|
||||
- [ ] **Parallel Test Execution**: Run specs in parallel
|
||||
- [ ] **Retry Logic**: Handle transient failures
|
||||
- [ ] **Custom Assertions**: Domain-specific test helpers
|
||||
- [ ] **Event Diff Tool**: Compare expected vs actual events
|
||||
- [ ] **Cleanup Automation**: Auto-cleanup after tests
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: Integration Tests Fail
|
||||
|
||||
**Symptoms:** Tests timeout or fail to connect
|
||||
|
||||
**Causes:**
|
||||
1. No relay running
|
||||
2. Wrong relay URL
|
||||
3. Firewall blocking connection
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Start relay
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
|
||||
# Verify relay is running
|
||||
curl http://localhost:7000
|
||||
|
||||
# Run tests
|
||||
cargo test -- --ignored
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue: Events Not Found in Query
|
||||
|
||||
**Symptoms:** Query returns empty even though events were sent
|
||||
|
||||
**Causes:**
|
||||
1. Wrong run ID (querying different run)
|
||||
2. Connection timing (query before event propagated)
|
||||
3. Tag mismatch (uppercase vs lowercase)
|
||||
|
||||
**Solution:**
|
||||
```rust
|
||||
// Use same config for send and query
|
||||
let config = AuditConfig::ci();
|
||||
|
||||
// Wait for event to propagate
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
|
||||
// Verify tags match exactly
|
||||
let t_tag = SingleLetterTag::lowercase(Alphabet::T); // Lowercase!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue: Build Fails in CI
|
||||
|
||||
**Symptoms:** `cargo build` fails with dependency errors
|
||||
|
||||
**Cause:** Not in Nix dev environment
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Enter Nix environment first
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
|
||||
# Then build
|
||||
cargo build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Configuration
|
||||
|
||||
```rust
|
||||
// CI mode
|
||||
let config = AuditConfig::ci();
|
||||
|
||||
// Production mode
|
||||
let config = AuditConfig::production("run-id");
|
||||
```
|
||||
|
||||
### Event Creation
|
||||
|
||||
```rust
|
||||
let event = AuditEventBuilder::new(&config, kind, content)
|
||||
.build(&keys)?;
|
||||
```
|
||||
|
||||
### Client Usage
|
||||
|
||||
```rust
|
||||
let client = AuditClient::new(config, keys);
|
||||
client.add_relay("ws://localhost:7000").await?;
|
||||
client.connect().await;
|
||||
let events = client.query().await?;
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Unit tests
|
||||
cargo test --lib
|
||||
|
||||
# Integration tests
|
||||
cargo test -- --ignored
|
||||
|
||||
# CLI
|
||||
cargo run -- audit --relay ws://localhost:7000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **GRASP Protocol**: https://gitworkshop.dev/danconwaydev.com/grasp
|
||||
- **NIP-01**: https://github.com/nostr-protocol/nips/blob/master/01.md
|
||||
- **NIP-34**: https://github.com/nostr-protocol/nips/blob/master/34.md
|
||||
- **grasp-audit README**: `grasp-audit/README.md`
|
||||
- **Tag Migration**: `docs/archive/2025-11-04-tag-migration.md`
|
||||
|
||||
---
|
||||
|
||||
*Last updated: November 4, 2025*
|
||||
*Status: Living document - update as grasp-audit evolves*
|
||||
@@ -0,0 +1,423 @@
|
||||
# Nix Flakes - Learnings and Gotchas
|
||||
|
||||
**Purpose:** Document Nix flake patterns, gotchas, and best practices learned during ngit-grasp development
|
||||
**Last Updated:** November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## Critical Gotchas
|
||||
|
||||
### Always Use `nix develop`, Not `nix-shell`
|
||||
|
||||
**Problem:** We use `flake.nix`, not `shell.nix`. Using `nix-shell` will fail or use the wrong environment.
|
||||
|
||||
```bash
|
||||
# ✅ Correct - for flake.nix
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
nix develop -c cargo build
|
||||
|
||||
# ❌ Wrong - for shell.nix (we don't use this)
|
||||
nix-shell
|
||||
nix-shell --run "cargo build"
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- `nix-shell` looks for `shell.nix` or `default.nix`
|
||||
- `nix develop` looks for `flake.nix`
|
||||
- We migrated from `shell.nix` to `flake.nix` on November 4, 2025
|
||||
|
||||
**Related:** See `docs/archive/2025-11-04-flake-migration.md`
|
||||
|
||||
---
|
||||
|
||||
## Flake Structure
|
||||
|
||||
### Our Standard Flake Pattern
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, rust-overlay, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
overlays = [ (import rust-overlay) ];
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
manifest = pkgs.lib.importTOML ./Cargo.toml;
|
||||
in with pkgs; {
|
||||
# Development shell
|
||||
devShells.default = mkShell {
|
||||
nativeBuildInputs = [
|
||||
rust-bin.stable.latest.default
|
||||
pkg-config
|
||||
gitlint
|
||||
];
|
||||
buildInputs = [
|
||||
openssl
|
||||
];
|
||||
shellHook = ''
|
||||
echo "🦀 Development environment loaded"
|
||||
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
|
||||
'';
|
||||
};
|
||||
|
||||
# Package output
|
||||
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = manifest.package.name;
|
||||
version = manifest.package.version;
|
||||
src = ./.;
|
||||
cargoLock = { lockFile = ./Cargo.lock; };
|
||||
buildInputs = [ openssl ];
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
doCheck = false; # Run tests separately
|
||||
};
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
1. **rust-overlay**: Provides latest stable Rust toolchain
|
||||
2. **flake-utils**: Cross-platform support helper
|
||||
3. **manifest**: Auto-read version from Cargo.toml
|
||||
4. **devShells.default**: Development environment
|
||||
5. **packages.default**: Buildable package
|
||||
|
||||
---
|
||||
|
||||
## Common Flake Commands
|
||||
|
||||
### Essential Commands
|
||||
|
||||
```bash
|
||||
# Enter development shell
|
||||
nix develop
|
||||
|
||||
# Run command in dev shell (one-off)
|
||||
nix develop -c cargo build
|
||||
|
||||
# Show flake outputs
|
||||
nix flake show
|
||||
|
||||
# Check flake validity
|
||||
nix flake check
|
||||
|
||||
# Update flake inputs (like updating dependencies)
|
||||
nix flake update
|
||||
|
||||
# Build the package directly
|
||||
nix build
|
||||
|
||||
# Run without installing
|
||||
nix run
|
||||
|
||||
# Show flake metadata
|
||||
nix flake metadata
|
||||
```
|
||||
|
||||
### Debugging Commands
|
||||
|
||||
```bash
|
||||
# Show detailed evaluation trace
|
||||
nix develop --show-trace
|
||||
|
||||
# Print flake evaluation
|
||||
nix eval .#devShells.x86_64-linux.default
|
||||
|
||||
# Check what's in the store
|
||||
nix path-info .#packages.x86_64-linux.default
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Subproject Flakes
|
||||
|
||||
### grasp-audit Has Its Own Flake
|
||||
|
||||
**Important:** `grasp-audit/` is a subproject with its own `flake.nix` and `Cargo.toml`.
|
||||
|
||||
```bash
|
||||
# ✅ Correct - enter grasp-audit environment
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
cargo build
|
||||
|
||||
# ❌ Wrong - can't build from root
|
||||
cd ngit-grasp
|
||||
cargo build # This won't find grasp-audit dependencies
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- Each Rust workspace needs its own Nix environment
|
||||
- Dependencies are project-specific
|
||||
- Flake inputs are locked per-project
|
||||
|
||||
---
|
||||
|
||||
## Migration from shell.nix to flake.nix
|
||||
|
||||
### What Changed
|
||||
|
||||
**Before (shell.nix):**
|
||||
```nix
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
rustc
|
||||
cargo
|
||||
openssl
|
||||
pkg-config
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**After (flake.nix):**
|
||||
- Locked inputs (reproducible)
|
||||
- Multi-output (dev shell + package)
|
||||
- Cross-platform by default
|
||||
- Better tooling integration
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. Create `flake.nix` with standard structure
|
||||
2. Run `nix flake check` to validate
|
||||
3. Update all documentation: `nix-shell` → `nix develop`
|
||||
4. Test that build works: `nix develop -c cargo build`
|
||||
5. Remove `shell.nix`
|
||||
6. Commit changes
|
||||
|
||||
**Reference:** See `docs/archive/2025-11-04-flake-migration.md`
|
||||
|
||||
---
|
||||
|
||||
## Benefits of Flakes
|
||||
|
||||
### Reproducibility
|
||||
|
||||
**Locked inputs** ensure everyone gets the same environment:
|
||||
|
||||
```bash
|
||||
# flake.lock contains exact commits
|
||||
$ cat flake.lock
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1698611440,
|
||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||
"rev": "23e89e0c8c5e2d9cf5b5e7c3e8e8e8e8e8e8e8e8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Everyone running `nix develop` gets **exactly** this version of nixpkgs.
|
||||
|
||||
### Multi-Output
|
||||
|
||||
Single flake provides:
|
||||
- **devShells.default**: Development environment
|
||||
- **packages.default**: Buildable package
|
||||
- **apps.default**: Runnable application (optional)
|
||||
|
||||
### Composability
|
||||
|
||||
Flakes can use other flakes as inputs:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
grasp-audit.url = "path:./grasp-audit";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: "error: getting status of '/nix/store/...': No such file or directory"
|
||||
|
||||
**Cause:** Flake inputs need to be updated or fetched
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
nix flake update
|
||||
nix develop
|
||||
```
|
||||
|
||||
### Issue: "error: experimental feature 'nix-command' is not enabled"
|
||||
|
||||
**Cause:** Nix flakes are experimental and need to be enabled
|
||||
|
||||
**Solution:**
|
||||
Add to `~/.config/nix/nix.conf`:
|
||||
```
|
||||
experimental-features = nix-command flakes
|
||||
```
|
||||
|
||||
### Issue: Changes to flake.nix not taking effect
|
||||
|
||||
**Cause:** Flake evaluation is cached
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Clear evaluation cache
|
||||
nix flake update
|
||||
# Or force re-evaluation
|
||||
nix develop --refresh
|
||||
```
|
||||
|
||||
### Issue: "error: cannot find flake 'flake:self' in the flake registries"
|
||||
|
||||
**Cause:** Not in a git repository or flake.nix not committed
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
git add flake.nix flake.lock
|
||||
git commit -m "Add flake"
|
||||
```
|
||||
|
||||
**Note:** Flakes require git. Uncommitted files are ignored by default.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Commit flake.lock
|
||||
|
||||
```bash
|
||||
git add flake.lock
|
||||
git commit -m "Update flake inputs"
|
||||
```
|
||||
|
||||
**Why:** Ensures reproducibility across machines and CI/CD
|
||||
|
||||
### 2. Use Specific Rust Versions When Needed
|
||||
|
||||
```nix
|
||||
# Latest stable (default)
|
||||
rust-bin.stable.latest.default
|
||||
|
||||
# Specific version
|
||||
rust-bin.stable."1.75.0".default
|
||||
|
||||
# Nightly
|
||||
rust-bin.nightly."2024-01-01".default
|
||||
```
|
||||
|
||||
### 3. Include Helpful Shell Hooks
|
||||
|
||||
```nix
|
||||
shellHook = ''
|
||||
echo "🦀 GRASP Audit development environment"
|
||||
echo ""
|
||||
echo "Common commands:"
|
||||
echo " cargo build - Build project"
|
||||
echo " cargo test - Run tests"
|
||||
echo " cargo run - Run binary"
|
||||
echo ""
|
||||
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
|
||||
'';
|
||||
```
|
||||
|
||||
### 4. Separate Build and Runtime Dependencies
|
||||
|
||||
```nix
|
||||
# Build-time only
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
rustc
|
||||
cargo
|
||||
];
|
||||
|
||||
# Runtime needed
|
||||
buildInputs = [
|
||||
openssl
|
||||
];
|
||||
```
|
||||
|
||||
### 5. Disable Tests in Package Build
|
||||
|
||||
```nix
|
||||
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||
# ...
|
||||
doCheck = false; # Run tests separately with cargo test
|
||||
};
|
||||
```
|
||||
|
||||
**Why:** Faster builds, tests run via `cargo test` in dev shell
|
||||
|
||||
---
|
||||
|
||||
## Workflow Examples
|
||||
|
||||
### Daily Development
|
||||
|
||||
```bash
|
||||
# Start work
|
||||
cd grasp-audit
|
||||
nix develop
|
||||
|
||||
# Inside nix shell
|
||||
cargo build
|
||||
cargo test
|
||||
cargo run -- --help
|
||||
|
||||
# Exit shell
|
||||
exit
|
||||
```
|
||||
|
||||
### CI/CD
|
||||
|
||||
```bash
|
||||
# One-off commands (no interactive shell)
|
||||
nix develop -c cargo build
|
||||
nix develop -c cargo test --lib
|
||||
nix develop -c cargo test -- --ignored
|
||||
```
|
||||
|
||||
### Building Release
|
||||
|
||||
```bash
|
||||
# Build package directly
|
||||
nix build
|
||||
|
||||
# Result is in ./result/bin/
|
||||
./result/bin/grasp-audit --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Nix Flakes Manual**: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html
|
||||
- **rust-overlay**: https://github.com/oxalica/rust-overlay
|
||||
- **flake-utils**: https://github.com/numtide/flake-utils
|
||||
- **Our Migration**: `docs/archive/2025-11-04-flake-migration.md`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
| Enter dev shell | `nix develop` |
|
||||
| Run one command | `nix develop -c <command>` |
|
||||
| Show outputs | `nix flake show` |
|
||||
| Validate flake | `nix flake check` |
|
||||
| Update inputs | `nix flake update` |
|
||||
| Build package | `nix build` |
|
||||
| Run package | `nix run` |
|
||||
|
||||
---
|
||||
|
||||
*Last updated: November 4, 2025*
|
||||
*Status: Living document - update as we learn more*
|
||||
@@ -0,0 +1,577 @@
|
||||
# nostr-sdk - Learnings and Patterns
|
||||
|
||||
**Purpose:** Document nostr-sdk usage patterns, upgrade notes, and gotchas
|
||||
**Last Updated:** November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## Current Version
|
||||
|
||||
**We use nostr-sdk 0.43.x (latest stable)**
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
nostr-sdk = "0.43"
|
||||
```
|
||||
|
||||
**Upgraded from:** 0.35.0 on November 4, 2025
|
||||
|
||||
---
|
||||
|
||||
## Critical Breaking Changes (0.35 → 0.43)
|
||||
|
||||
### 1. EventBuilder API Changed
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
let event = EventBuilder::new(kind, content, tags)
|
||||
.to_event(keys)?;
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
let event = EventBuilder::new(kind, content)
|
||||
.tags(tags)
|
||||
.sign_with_keys(keys)?;
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- ❌ Removed `tags` parameter from constructor
|
||||
- ✅ Use `.tags()` builder method instead
|
||||
- ❌ Removed `.to_event()` method
|
||||
- ✅ Use `.sign_with_keys()` instead (more descriptive)
|
||||
|
||||
---
|
||||
|
||||
### 2. Client Ownership of Keys
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
let keys = Keys::generate();
|
||||
let client = Client::new(&keys); // Reference
|
||||
// keys still available
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
let keys = Keys::generate();
|
||||
let client = Client::new(keys.clone()); // Ownership
|
||||
// Need to clone if we want to keep keys
|
||||
```
|
||||
|
||||
**Why:** Allows Client to own the signer, enabling more flexible signer types.
|
||||
|
||||
---
|
||||
|
||||
### 3. Relay Status Check No Longer Async
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
if relay.is_connected().await {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
if relay.is_connected() { // No await!
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** Status check doesn't require async operation.
|
||||
|
||||
---
|
||||
|
||||
### 4. Query API Redesigned
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
let events = client
|
||||
.get_events_of(vec![filter], EventSource::relays(Some(timeout)))
|
||||
.await?;
|
||||
// Returns Vec<Event>
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
let events = client
|
||||
.fetch_events(filter, timeout)
|
||||
.await?;
|
||||
// Returns Events (iterable collection)
|
||||
|
||||
// Convert to Vec if needed
|
||||
let vec: Vec<Event> = events.into_iter().collect();
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- ❌ Removed `get_events_of()` method
|
||||
- ✅ Use `fetch_events()` instead
|
||||
- ❌ Removed `EventSource` parameter (confusing)
|
||||
- ✅ Direct timeout parameter
|
||||
- ❌ Single filter instead of `Vec<Filter>`
|
||||
- ✅ Returns `Events` type instead of `Vec<Event>`
|
||||
|
||||
---
|
||||
|
||||
### 5. Filter Custom Tags Simplified
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
filter.custom_tag(tag, ["value"])
|
||||
filter.custom_tag(tag, [&string_ref])
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
filter.custom_tag(tag, "value")
|
||||
filter.custom_tag(tag, &string_ref)
|
||||
```
|
||||
|
||||
**Why:** Simplified API for the common case of single tag value.
|
||||
|
||||
---
|
||||
|
||||
### 6. Send Event Takes Reference
|
||||
|
||||
**Before (0.35):**
|
||||
```rust
|
||||
let event_id = client.send_event(event).await?;
|
||||
```
|
||||
|
||||
**After (0.43):**
|
||||
```rust
|
||||
let output = client.send_event(&event).await?;
|
||||
let event_id = *output.id();
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Takes `&Event` instead of `Event` (can reuse events)
|
||||
- Returns `SendEventOutput` instead of `EventId`
|
||||
- Need to call `.id()` to get the event ID
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Creating and Signing Events
|
||||
|
||||
```rust
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
// Generate keys
|
||||
let keys = Keys::generate();
|
||||
|
||||
// Create event
|
||||
let event = EventBuilder::new(Kind::TextNote, "Hello Nostr!")
|
||||
.tags(vec![
|
||||
Tag::custom(TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::T)),
|
||||
vec!["nostr"]),
|
||||
])
|
||||
.sign_with_keys(&keys)?;
|
||||
|
||||
// Send event
|
||||
let output = client.send_event(&event).await?;
|
||||
println!("Event ID: {}", output.id());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Creating Custom Tags
|
||||
|
||||
```rust
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
// Single letter tag (like "t" for topics)
|
||||
let t_tag = SingleLetterTag::lowercase(Alphabet::T);
|
||||
let tag = Tag::custom(
|
||||
TagKind::SingleLetter(t_tag),
|
||||
vec!["my-topic"]
|
||||
);
|
||||
|
||||
// Custom multi-letter tag
|
||||
let tag = Tag::custom(
|
||||
TagKind::Custom("custom-tag".to_string()),
|
||||
vec!["value1", "value2"]
|
||||
);
|
||||
|
||||
// Hashtag (convenience method)
|
||||
let tag = Tag::hashtag("nostr"); // Creates ["t", "nostr"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Querying Events
|
||||
|
||||
```rust
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
// Build filter
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::TextNote)
|
||||
.custom_tag(
|
||||
SingleLetterTag::lowercase(Alphabet::T),
|
||||
"my-topic"
|
||||
)
|
||||
.since(Timestamp::now() - Duration::from_secs(3600)); // Last hour
|
||||
|
||||
// Query events
|
||||
let timeout = Duration::from_secs(10);
|
||||
let events = client.fetch_events(filter, timeout).await?;
|
||||
|
||||
// Process events
|
||||
for event in events.into_iter() {
|
||||
println!("Event: {}", event.id());
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Multiple Filters
|
||||
|
||||
Since `fetch_events()` takes a single filter, combine multiple queries:
|
||||
|
||||
```rust
|
||||
// Option 1: Fetch separately and combine
|
||||
let mut all_events = Vec::new();
|
||||
for filter in filters {
|
||||
let events = client.fetch_events(filter, timeout).await?;
|
||||
all_events.extend(events.into_iter());
|
||||
}
|
||||
|
||||
// Option 2: Use subscription (more efficient)
|
||||
let subscription_id = SubscriptionId::new("my-sub");
|
||||
client.subscribe(filters, None).await?;
|
||||
|
||||
// Handle events via notification handler
|
||||
let mut notifications = client.notifications();
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
if let RelayPoolNotification::Event { event, .. } = notification {
|
||||
println!("Event: {}", event.id());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Client Setup with Relay
|
||||
|
||||
```rust
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
// Create keys
|
||||
let keys = Keys::generate();
|
||||
|
||||
// Create client
|
||||
let client = Client::new(keys.clone());
|
||||
|
||||
// Add relay
|
||||
client.add_relay("wss://relay.example.com").await?;
|
||||
|
||||
// Connect
|
||||
client.connect().await;
|
||||
|
||||
// Wait for connection
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// Check connection
|
||||
if client.relay("wss://relay.example.com")
|
||||
.await?
|
||||
.is_connected()
|
||||
{
|
||||
println!("Connected!");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Unit Tests (No Relay Required)
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_event_creation() {
|
||||
let keys = Keys::generate();
|
||||
let event = EventBuilder::new(Kind::TextNote, "test")
|
||||
.sign_with_keys(&keys)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(event.kind(), Kind::TextNote);
|
||||
assert_eq!(event.content(), "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tag_creation() {
|
||||
let t_tag = SingleLetterTag::lowercase(Alphabet::T);
|
||||
let tag = Tag::custom(
|
||||
TagKind::SingleLetter(t_tag),
|
||||
vec!["test-topic"]
|
||||
);
|
||||
|
||||
// Verify tag structure
|
||||
assert_eq!(tag.as_vec()[0], "t");
|
||||
assert_eq!(tag.as_vec()[1], "test-topic");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Integration Tests (Relay Required)
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // Requires running relay
|
||||
async fn test_send_and_receive() -> Result<()> {
|
||||
// Setup
|
||||
let keys = Keys::generate();
|
||||
let client = Client::new(keys.clone());
|
||||
client.add_relay("ws://localhost:7000").await?;
|
||||
client.connect().await;
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// Send event
|
||||
let event = EventBuilder::new(Kind::TextNote, "test")
|
||||
.sign_with_keys(&keys)?;
|
||||
let output = client.send_event(&event).await?;
|
||||
|
||||
// Query it back
|
||||
let filter = Filter::new()
|
||||
.id(*output.id());
|
||||
let events = client.fetch_events(filter, Duration::from_secs(5)).await?;
|
||||
|
||||
assert_eq!(events.len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Running integration tests:**
|
||||
```bash
|
||||
# Start relay first
|
||||
docker run --rm -p 7000:7000 scsibug/nostr-rs-relay
|
||||
|
||||
# Run tests
|
||||
cargo test -- --ignored
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Gotchas
|
||||
|
||||
### 1. Event Validation Failures
|
||||
|
||||
**Problem:** Events fail validation with cryptic errors
|
||||
|
||||
**Common Causes:**
|
||||
- Invalid signature (wrong keys used)
|
||||
- Invalid event ID (content/tags changed after signing)
|
||||
- Invalid timestamp (too far in future/past)
|
||||
|
||||
**Solution:**
|
||||
```rust
|
||||
// Always sign AFTER setting all fields
|
||||
let event = EventBuilder::new(kind, content)
|
||||
.tags(tags) // Set tags first
|
||||
.sign_with_keys(&keys)?; // Sign last
|
||||
|
||||
// Don't modify event after signing!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Filter Not Matching Events
|
||||
|
||||
**Problem:** Query returns no events even though they exist
|
||||
|
||||
**Common Causes:**
|
||||
- Tag kind mismatch (uppercase vs lowercase)
|
||||
- Wrong filter field (using `.author()` when you need `.authors()`)
|
||||
- Timeout too short
|
||||
|
||||
**Solution:**
|
||||
```rust
|
||||
// Be explicit about tag kinds
|
||||
let t_tag = SingleLetterTag::lowercase(Alphabet::T); // Lowercase!
|
||||
|
||||
// Use correct filter methods
|
||||
let filter = Filter::new()
|
||||
.authors(vec![keys.public_key()]) // Note: plural
|
||||
.kinds(vec![Kind::TextNote]); // Note: plural
|
||||
|
||||
// Increase timeout for slow relays
|
||||
let timeout = Duration::from_secs(10);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Connection Timing Issues
|
||||
|
||||
**Problem:** Events fail to send or queries return empty
|
||||
|
||||
**Cause:** Client not fully connected to relay
|
||||
|
||||
**Solution:**
|
||||
```rust
|
||||
// Connect
|
||||
client.connect().await;
|
||||
|
||||
// Wait for connection to establish
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// Verify connection
|
||||
let relay = client.relay("wss://relay.example.com").await?;
|
||||
if !relay.is_connected() {
|
||||
return Err("Not connected".into());
|
||||
}
|
||||
|
||||
// Now safe to send/query
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Clone Keys When Creating Client
|
||||
|
||||
**Problem:** Can't use keys after creating client
|
||||
|
||||
**Cause:** Client takes ownership in 0.43+
|
||||
|
||||
**Solution:**
|
||||
```rust
|
||||
// Clone keys if you need them later
|
||||
let keys = Keys::generate();
|
||||
let client = Client::new(keys.clone()); // Clone!
|
||||
|
||||
// Now can still use keys
|
||||
let pubkey = keys.public_key();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Reuse Clients
|
||||
|
||||
```rust
|
||||
// ✅ Good - single client
|
||||
let client = Client::new(keys);
|
||||
client.add_relay("wss://relay1.com").await?;
|
||||
client.add_relay("wss://relay2.com").await?;
|
||||
client.connect().await;
|
||||
|
||||
// ❌ Bad - multiple clients
|
||||
for relay in relays {
|
||||
let client = Client::new(keys.clone()); // Wasteful!
|
||||
client.add_relay(relay).await?;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Use Subscriptions for Live Updates
|
||||
|
||||
```rust
|
||||
// ✅ Good for live updates - subscription
|
||||
let filters = vec![Filter::new().kind(Kind::TextNote)];
|
||||
client.subscribe(filters, None).await?;
|
||||
|
||||
let mut notifications = client.notifications();
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
// Handle events as they arrive
|
||||
}
|
||||
|
||||
// ❌ Bad for live updates - polling
|
||||
loop {
|
||||
let events = client.fetch_events(filter, timeout).await?;
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Batch Event Creation
|
||||
|
||||
```rust
|
||||
// ✅ Good - reuse keys
|
||||
let keys = Keys::generate();
|
||||
let events: Vec<Event> = (0..100)
|
||||
.map(|i| {
|
||||
EventBuilder::new(Kind::TextNote, format!("Message {}", i))
|
||||
.sign_with_keys(&keys)
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
// ❌ Bad - regenerate keys
|
||||
let events: Vec<Event> = (0..100)
|
||||
.map(|i| {
|
||||
let keys = Keys::generate(); // Wasteful!
|
||||
EventBuilder::new(Kind::TextNote, format!("Message {}", i))
|
||||
.sign_with_keys(&keys)
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist (0.35 → 0.43)
|
||||
|
||||
When upgrading from 0.35 to 0.43:
|
||||
|
||||
- [ ] Update `Cargo.toml`: `nostr-sdk = "0.43"`
|
||||
- [ ] Fix `EventBuilder::new()` - remove tags parameter
|
||||
- [ ] Fix `EventBuilder::to_event()` → `sign_with_keys()`
|
||||
- [ ] Fix `Client::new()` - clone keys instead of reference
|
||||
- [ ] Fix `Relay::is_connected()` - remove `.await`
|
||||
- [ ] Fix `Client::get_events_of()` → `fetch_events()`
|
||||
- [ ] Remove `EventSource::relays()` usage
|
||||
- [ ] Fix `Filter::custom_tag()` - single value instead of array
|
||||
- [ ] Fix `Client::send_event()` - pass reference, handle `SendEventOutput`
|
||||
- [ ] Update tests
|
||||
- [ ] Verify all builds pass
|
||||
- [ ] Run integration tests
|
||||
|
||||
**Reference:** See `docs/archive/2025-11-04-nostr-sdk-upgrade.md`
|
||||
|
||||
---
|
||||
|
||||
## Useful Resources
|
||||
|
||||
- **nostr-sdk docs**: https://docs.rs/nostr-sdk/0.43.0
|
||||
- **rust-nostr GitHub**: https://github.com/rust-nostr/nostr
|
||||
- **NIPs**: https://github.com/nostr-protocol/nips
|
||||
- **NIP-01 (Events)**: https://github.com/nostr-protocol/nips/blob/master/01.md
|
||||
- **NIP-34 (Git)**: https://github.com/nostr-protocol/nips/blob/master/34.md
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Code |
|
||||
|------|------|
|
||||
| Create event | `EventBuilder::new(kind, content).sign_with_keys(&keys)?` |
|
||||
| Add tags | `.tags(vec![tag1, tag2])` |
|
||||
| Custom tag | `Tag::custom(TagKind::SingleLetter(t), vec!["value"])` |
|
||||
| Create client | `Client::new(keys.clone())` |
|
||||
| Add relay | `client.add_relay("wss://...").await?` |
|
||||
| Connect | `client.connect().await` |
|
||||
| Send event | `client.send_event(&event).await?` |
|
||||
| Query events | `client.fetch_events(filter, timeout).await?` |
|
||||
| Subscribe | `client.subscribe(filters, None).await?` |
|
||||
|
||||
---
|
||||
|
||||
*Last updated: November 4, 2025*
|
||||
*Status: Living document - update as nostr-sdk evolves*
|
||||
Reference in New Issue
Block a user