docs(adr): ADR-0009: Audience Scopes for Filtered Documentation Views
@aRustyDev ya está trabajando en esto.
Desde el 5/1/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
ADR-0009: Audience Scopes for Filtered Documentation Views
Status
Accepted
Context
Documentation often serves multiple audiences with different needs:
- Developers need API references, code examples
- Managers need architecture overviews, decision rationale
- SREs need operational runbooks, monitoring guides
- Beginners need tutorials, getting started guides
Users authorized to see multiple audiences' content want to focus on one audience at a time, filtering navigation and search to that subset.
Decision Drivers
- Focus - Users shouldn't wade through irrelevant content
- Security - Users shouldn't see content they're not authorized for
- Simplicity - Minimal configuration burden on content authors
- Performance - Fast filtering without server-side computation
Key Decisions
1. URL Structure: Query Parameter
Decision: Use query parameter ?scope=developers instead of path prefix.
/docs/chapter-1?scope=developers ✅ Chosen
/docs/developers/chapter-1 ❌ Rejected
Rationale:
- Content URL is stable regardless of scope
- Better caching (same page, different lens)
- No path rewriting needed for internal links
- Scope is a "view preference" not a location
2. No Scope Inheritance
Decision: Each page explicitly declares its scopes. No inheritance from parent chapters.
# Every page must declare its scopes
---
scopes: [developers, sre]
---
Rationale:
- Explicit is better than implicit
- Easy to audit (grep for scopes)
- No surprises when restructuring chapters
- Avoids complex inheritance resolution
3. Separate Search Indexes Per Scope
Decision: Generate scope-specific search indexes at build time.
book/htmx/
├── search-index.json # Full index
├── search-index.developers.json # Developer scope only
├── search-index.managers.json # Manager scope only
Rationale:
- Prevents information leakage (titles, snippets)
- No runtime filtering overhead
- Unauthorized content never sent to client
4. Runtime AuthZ as Defense in Depth
Decision: Apply authorization filtering at runtime even with scope-specific indexes.
Rationale:
- Build-time scope filtering may drift from auth rules
- Defense in depth catches edge cases
- Minimal performance impact
Implementation
Frontmatter Schema
---
title: API Reference
scopes:
- developers
- sre
auth:
access: roles
roles: [developer, sre]
---
Note: scopes and auth are independent:
scopescontrols navigation filtering (user's choice)authcontrols access permissions (admin's policy)
Configuration
[output.htmx.scopes]
enabled = true
available = ["all", "developers", "managers", "sre"]
default = "all"
[output.htmx.search]
scope-indexes = true # Generate per-scope indexes
authz-filter = true # Runtime auth check
global-scopes = ["all"] # Always in main index
Build Output
book/htmx/
├── manifest.json # Full manifest
├── manifest.developers.json # Navigation for developer scope
├── manifest.managers.json # Navigation for manager scope
├── search-index.json # Full search index
├── search-index.developers.json
└── search-index.managers.json
Server Implementation
app.get('/docs/*', async (c) => {
const scope = c.req.query('scope') || 'all';
const user = c.get('user');
// Anonymous users forced to 'all' scope
const effectiveScope = user ? scope : 'all';
// Load scope-specific manifest for navigation
const manifest = manifests[effectiveScope] || manifests.all;
// Load scope-specific search index
const searchIndex = searchIndexes[effectiveScope] || searchIndexes.all;
return c.html(await render('layout.html', {
page,
navigation: manifest.navigation,
currentScope: effectiveScope,
availableScopes: user ? manifest.scopes.available : ['all']
}));
});
Scope Switcher Partial
<!-- partials/scope-switcher.html -->
<div class="scope-switcher" id="scope-switcher">
<label>View:</label>
<select hx-get="{{ current_path }}"
hx-target="body"
hx-push-url="true"
name="scope">
{% for scope in available_scopes %}
<option value="{{ scope }}"
{% if scope == current_scope %}selected{% endif %}>
{{ scope | title }}
</option>
{% endfor %}
</select>
</div>
Scope vs Authorization
| Aspect | Scope | Authorization |
|---|---|---|
| Question | What view do I want? | Can I access this page? |
| Who decides | User (their preference) | Admin (policy) |
| Enforcement | Navigation + search filtering | Access denied / redirect |
| Failure mode | Page hidden from nav/search | 403 or redirect to login |
A page can be:
- In scope
developersbut require rolesenior-developer - Visible in navigation but return 403 on access
- In multiple scopes with the same authorization
Consequences
Positive
- Users can focus on relevant content
- Search results are scoped appropriately
- No information leakage via search
- Clean URL structure
- Simple explicit configuration
Negative
- More build output (one manifest/index per scope)
- Authors must tag every page with scopes
- No automatic scope inference
Mitigation
- Default scope
allcatches untagged pages - Linting can warn about missing scope declarations
- Build-time validation ensures consistency
Alternatives Considered
Path-Based Scopes (/docs/developers/page)
Rejected because:
- Breaks internal links (need rewriting)
- Complicates caching
- Scope is a lens, not a location
Scope Inheritance from Parent Chapters
Rejected because:
- Implicit behavior causes surprises
- Hard to audit
- Restructuring chapters changes scope unexpectedly
Single Index with Runtime Filtering
Rejected because:
- Leaks page titles and snippets
- Runtime overhead on every search
- Defense-only approach (no build-time separation)
References
- Lenguaje dominante
- Rust
- Estrellas
- 0
- Forks
- 1
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de aRustyDev/mdbook-htmx
-
feat(ci): Implement cross-repo-validate compliance for docs syncQuizá libre de nuevo @aRustyDev la tomó hace 285 días y no hay ningún pull request abierto. Abiertoci enhancement
aRustyDev/mdbook-htmx#58 · 1 comentario · 1 asignado ·
-
docs(htmx): Explain how HTMX worksQuizá libre de nuevo @aRustyDev la tomó hace 287 días y no hay ningún pull request abierto. Abierto
aRustyDev/mdbook-htmx#48 · 3 comentarios · 1 asignado ·
-
docs(mdbook-htmx): Parent Issue for documentationQuizá libre de nuevo @aRustyDev la tomó hace 287 días y no hay ningún pull request abierto. Abiertodocumentation
aRustyDev/mdbook-htmx#47 · 1 asignado ·
-
docs(examples): Secure Self-Hosted Meilisearch via CloudflareQuizá libre de nuevo @aRustyDev la tomó hace 287 días y no hay ningún pull request abierto. Abiertodocumentation
aRustyDev/mdbook-htmx#46 · 1 asignado ·
-
docs(examples): Kubernetes with MeilisearchQuizá libre de nuevo @aRustyDev la tomó hace 287 días y no hay ningún pull request abierto. Abiertodocumentation
aRustyDev/mdbook-htmx#45 · 1 asignado ·
Todos los issues de aRustyDev/mdbook-htmx
Issues similares
-
engine-rs P3 review-deferral wasm
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
maniator/verticopolis#1154 ·
Los mantenedores suelen responder en 1 día
-
FFMPEG_BIN/FFPROBE_BIN are always overridden, so a working system ffmpeg cannot be usedPosiblemente ocupada @Bilker1422 la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Stremio/stremio-service#116 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
sims1253/ry#720 · 1 comentario ·
Los mantenedores suelen responder en 1 día