• Townie
    AI
  • Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
drewmcdonald

drewmcdonald

inbox

Public
Like
inbox
Home
Code
11
.vtignore
AttachmentService.ts
CLAUDE.md
InboxService.ts
PROJECT.md
TODO.md
auth.ts
db.ts
deno.json
E
emailHandler.ts
H
httpHandler.ts
Branches
1
Pull requests
Remixes
History
Environment variables
2
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
PROJECT.md
Code
/
PROJECT.md
Search
…
PROJECT.md

Obsidian Inbox

A Val.town service that acts as a temporary inbox for raw information before it's processed into an Obsidian vault.

Purpose

This service provides a buffer layer between various information sources (email, HTTP POST) and Obsidian vault management systems. It:

  • Receives raw information from multiple sources
  • Stores unprocessed items with attachments
  • Provides a simple API for clients to retrieve and process items
  • Manages the lifecycle of inbox items (unprocessed → processed → deleted)

Architecture

Platform

  • Runtime: Deno on Val.town serverless environment
  • Database: SQLite (Val.town standard library)
  • Storage: Blob storage (Val.town standard library)
  • Language: TypeScript

Core Components

1. Input Handlers

  • emailHandler.ts: Email trigger that processes incoming emails

    • Validates sender against allowlist
    • Extracts email content and metadata
    • Saves attachments to blob storage
    • Creates inbox record
  • httpHandler.ts: HTTP API using Hono framework

    • GET / - Retrieve all unprocessed items
    • POST / - Create new inbox item (for non-email sources)
    • PATCH /:id/processed - Mark item as processed (returns 404 if record not found)
    • GET /:recordId/attachments - Get attachment metadata for a record

2. Services

  • InboxService.ts: Core data persistence for inbox records

    • Stores records in SQLite with UUID keys
    • Tracks processing state (unprocessed/processed timestamp)
    • Provides lifecycle operations (save, markProcessed, retrieveUnprocessed)
    • Auto-deletes processed records after 30 days
  • AttachmentService.ts: Blob storage for email attachments

    • Stores binary data and metadata separately
    • Supports get/save/delete operations
    • Uses UUID keys for blob identification
  • auth.ts: Authentication and authorization

    • Bearer token auth for HTTP endpoints (via API_TOKEN env var)
    • Email sender allowlist (via INCOMING_EMAIL_ALLOWLIST env var)

Data Model

InboxRecord

{ raw: string, // Full text content (email body, etc.) summary?: string, // Optional summary from: string, // Source identifier (email address, etc.) subject: string, // Subject line or title date: Date, // When item was received attachmentBlobIds: string[] // References to blob storage }

Stored in SQLite table inbox_records with:

  • id (UUID primary key)
  • record (JSON-serialized InboxRecord)
  • created_at (timestamp)
  • processed_at (nullable timestamp)

AttachmentMetadata

{ filename: string, contentType: string, size: number, data: ArrayBuffer // The actual binary content }

Stored in blob storage as:

  • {uuid} - binary data
  • {uuid}:meta - JSON metadata

Processing Lifecycle

  1. Ingestion: Item arrives via email or HTTP POST
  2. Storage: Saved to SQLite with processed_at = null
  3. Retrieval: Client calls GET / to fetch unprocessed items
  4. Processing: Client transforms item into Obsidian note(s) (outside this service)
  5. Completion: Client calls PATCH /:id/processed to mark as done
  6. Cleanup: After 30 days, processed records auto-delete (including attachments)

Note: Clients are responsible for idempotency. If processing fails partway through, the client should handle re-processing gracefully.

Environment Variables

Required configuration via Val.town secrets:

  • API_TOKEN - Bearer token for HTTP API authentication
  • INCOMING_EMAIL_ALLOWLIST - Comma-separated email addresses allowed to send

Client Integration

Clients that consume this inbox should:

  1. Poll GET / endpoint for unprocessed items (or use webhooks if added)
  2. Get attachment metadata via GET /:recordId/attachments and download blobs
  3. Process items into Obsidian vault format
  4. Mark each item as processed via PATCH /:id/processed
  5. Handle failures gracefully (service doesn't retry)

Client type is TBD - may be MCP servers, custom scripts, or other automation.

Development Guidelines

Val.town Specifics

  • All code must be compatible with Deno runtime
  • Use https://esm.sh for npm imports
  • Storage limited to SQLite + blob storage (no filesystem)
  • See .cursor/rules/townie.mdc for full Val.town development guidelines

Common Modification Patterns

Schema Changes:

  • Modify inboxRecordSchema in InboxService.ts
  • Change table name (e.g., inbox_records → inbox_records_2) to avoid migration issues
  • Update both email and HTTP handlers if needed

Adding Input Sources:

  1. Create new handler file (e.g., webhookHandler.ts)
  2. Map to appropriate trigger type (HTTP, cron, etc.)
  3. Transform input to InboxRecord format
  4. Call InboxService.save()

Processing Logic Changes:

  • Most business logic lives in handlers (emailHandler.ts, httpHandler.ts)
  • Keep services (InboxService, AttachmentService) focused on data operations
  • Prefer small, focused functions over complex class hierarchies

Testing

  • Manual testing via Val.town web interface
  • No formal test suite currently
  • Focus on edge cases: malformed emails, missing attachments, auth failures

Known TODOs

  • Error handling and retry logic for failed processing
  • Consider webhook/push notification when new items arrive (vs polling)
  • Finalize client architecture (MCP vs custom scripts)
  • Evaluate if 30-day retention is appropriate for all use cases

File Reference

.
├── emailHandler.ts       # Email trigger entry point
├── httpHandler.ts        # HTTP API entry point (Hono app)
├── service.ts            # Main service entry point (routes to handlers)
├── InboxService.ts       # Inbox record persistence
├── AttachmentService.ts  # Blob storage for attachments
├── auth.ts               # Authentication/authorization
├── deno.json             # Deno/Val.town configuration
└── .cursor/rules/
    └── townie.mdc        # Val.town development guidelines

Design Principles

  1. Simple buffer: This is NOT a full email client or note-taking app. It's a temporary holding area.
  2. Client-side processing: Complex transformations happen in clients, not here.
  3. Stateless: Each inbox item is independent. No complex workflows or state machines.
  4. Val.town native: Leverage platform services (SQLite, blob, email) rather than external dependencies.
  5. Fail-fast: Let errors bubble up with context rather than hiding them.
FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.