# How to Write a Wiki Page Through the Typed Boundary in ai-memory

> Learn how to write a wiki page in ai-memory using the Wiki::write_page method. This guide explains the WritePageRequest struct for atomic, sanitized persistence. Get started today.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: how-to-guide
- Published: 2026-09-11

---

**The only public way to create or modify a wiki page in ai-memory is through the `Wiki::write_page` method, which accepts a single `WritePageRequest` struct that encapsulates all identifiers, metadata, and body content for atomic, sanitized persistence.**

The ai-memory repository provides a type-safe boundary for wiki operations that guarantees data consistency, automatic secret scrubbing, and admission webhook processing. By funneling all write operations through the `WritePageRequest` type defined in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs), the system ensures that every page creation or update follows a strict validation and persistence pipeline.

## Understanding the Typed Boundary

The typed boundary pattern in ai-memory forces all wiki mutations through a single, strongly-typed interface. This design prevents partial writes and guarantees that every page is properly attributed, sanitized, and indexed.

### The WritePageRequest Struct

The `WritePageRequest` struct is defined at **lines 2225–2231** in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs). It aggregates all necessary context for a write operation into one immutable request object:

- **workspace_id** (`WorkspaceId`): Identifier of the owning workspace
- **project_id** (`ProjectId`): Identifier of the owning project
- **path** (`PagePath`): Relative wiki path (e.g., `"notes/example.md"`)
- **frontmatter**: Optional JSON front-matter for title, tags, and metadata
- **body**: Markdown content without front-matter
- **tier** (`Tier`): Classification used for indexing (e.g., `Tier::Semantic`)
- **pinned**: Boolean indicating whether the page is pinned
- **title**: Optional pre-derived title for batch writes
- **admission_ctx**: Optional admission-webhook context for custom validation
- **author_id** (`UserId`): Multi-user attribution field
- **actor** (`ActorContext`): Caller description (anonymous or authenticated)

## Core Implementation Details

The `write_page` method implementation resides at **lines 2027–2030** in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs). When invoked, it executes a seven-step pipeline that ensures data integrity before any persistence occurs:

1. **Path validation**: `req.path.ensure_portable()` guarantees a portable, cross-platform filename
2. **Body sanitisation**: `self.sanitizer.scrub(&body)` removes secrets and sensitive data
3. **Front-matter stamping**: `stamp_last_modified_by` injects the actor context into metadata
4. **Admission chain**: If configured in `admission_ctx`, custom webhooks run synchronously and may mutate the request
5. **Second sanitisation**: Post-webhook mutations are scrubbed again to prevent data leakage
6. **Canonicalisation & conformance**: Front-matter is normalized to OKF (Open Knowledge Format) standards in [`crates/ai-memory-wiki/src/markdown.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/markdown.rs)
7. **Atomic write**: The markdown file and SQLite store row are updated in a single transaction

## Writing a Wiki Page in Practice

Below is a minimal Rust example that creates a wiki page through the typed boundary. This demonstrates proper initialization of the `WritePageRequest` and invocation of `Wiki::write_page`:

```rust
use ai_memory_wiki::{Wiki, WritePageRequest};
use ai_memory_core::{
    ids::{WorkspaceId, ProjectId},
    path::PagePath,
    tier::Tier,
    actor::ActorContext,
};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the Wiki instance according to your deployment configuration
    let wiki = Wiki::new().await?;

    // Assemble the typed request
    let request = WritePageRequest {
        workspace_id: WorkspaceId::new("default"),
        project_id: ProjectId::new("my_project"),
        path: PagePath::new("notes/example.md")?,
        frontmatter: json!({
            "title": "Example page",
            "tags": ["example", "demo"],
            "tier": "semantic"
        }),
        body: String::from("This is the markdown **body** of the page."),
        tier: Tier::Semantic,
        pinned: false,
        title: None,
        admission_ctx: None,          // No custom admission chain
        author_id: None,              // Anonymous write
        actor: ActorContext::anonymous(),
    };

    // Execute the write through the typed boundary
    let page_id = wiki.write_page(request).await?;
    println!("Page written, id = {}", page_id);
    Ok(())
}

```

The `page_id` returned by `write_page` represents the unique identifier assigned to the newly created or updated page in the SQLite store.

## Admission Webhooks and Sanitization

The typed boundary automatically handles security concerns through its integrated sanitization layer. Before any content reaches the filesystem or database, the `self.sanitizer.scrub()` method defined in the wiki module processes the body text to remove potential secrets or sensitive patterns.

For organizations requiring custom validation logic, the `admission_ctx` field accepts an `AdmissionContext` struct that triggers webhook processing between the first and second sanitization passes. This allows external services to:

- Enforce naming conventions on the `path` field
- Validate that `tier` classifications match organizational policies
- Auto-generate or modify `frontmatter` based on content analysis

Webhook handlers run synchronously and may mutate the request before the final sanitization and atomic write occur.

## Summary

- **Single entry point**: All wiki writes must use `Wiki::write_page` with a `WritePageRequest` struct defined in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs)
- **Comprehensive validation**: The implementation validates paths, scrubs secrets twice, and runs admission webhooks before persisting data
- **Atomic persistence**: Markdown files and SQLite entries are updated in a single transaction to prevent data inconsistency
- **Flexible attribution**: The `ActorContext` and `author_id` fields support both anonymous and authenticated multi-user scenarios
- **Standards compliance**: Front-matter is automatically canonicalized to OKF standards during the write pipeline

## Frequently Asked Questions

### What is the typed boundary in ai-memory?

The typed boundary is a design pattern that requires all wiki write operations to pass through a single, strongly-typed API—specifically the `WritePageRequest` struct consumed by `Wiki::write_page`. This ensures that every page creation includes mandatory fields like `workspace_id`, `project_id`, and `path`, while automatically applying sanitization and admission logic before persistence.

### Can I write wiki pages without using the WritePageRequest struct?

No. According to the ai-memory source code, `Wiki::write_page` is the only public method for creating or modifying wiki pages, and it exclusively accepts the `WritePageRequest` type. Attempting to bypass this boundary would require modifying the private internals of [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs), breaking the consistency guarantees provided by the sanitization and atomic write pipeline.

### How does admission webhook processing work during a write?

If you supply an `AdmissionContext` in the `admission_ctx` field of your request, the `write_page` method pauses execution after initial sanitization to run your configured webhook chain. These hooks can inspect and mutate the request before the second sanitization pass and final atomic write. This happens synchronously at **lines 2027–2030** in [`wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs), allowing real-time validation or metadata enrichment before persistence.

### What happens if the body contains sensitive information?

The typed boundary automatically scrubs the body content using `self.sanitizer.scrub(&body)` before any file write occurs. If admission webhooks modify the content, a second sanitization pass runs immediately after. This double-scrubbing approach, implemented in the core wiki module, ensures that secrets never reach the markdown files or SQLite store even if webhooks inject additional text.