docs(adr): ADR-0009: Audience Scopes for Filtered Documentation Views
@aRustyDev ci sta già lavorando.
Dal 5/1/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
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
- Lingua principale
- Rust
- Stelle
- 0
- Fork
- 1
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di aRustyDev/mdbook-htmx
-
feat(ci): Implement cross-repo-validate compliance for docs syncForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Apertaci enhancement
aRustyDev/mdbook-htmx#58 · 1 commento · 1 assegnatario ·
-
docs(htmx): Explain how HTMX worksForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Aperta
aRustyDev/mdbook-htmx#48 · 3 commenti · 1 assegnatario ·
-
docs(mdbook-htmx): Parent Issue for documentationForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#47 · 1 assegnatario ·
-
docs(examples): Secure Self-Hosted Meilisearch via CloudflareForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#46 · 1 assegnatario ·
-
docs(examples): Kubernetes with MeilisearchForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#45 · 1 assegnatario ·
Tutte le issue di aRustyDev/mdbook-htmx
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] ComponentType classifies 0x8A8A-0xEAEA as GREASE; mls-extensions §7.5 makes them private useAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno
-
The book has a Nix install section that chelis.ch lacks, and it names two of the three systemsApertaarea:ecosystem documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
Chelis-Lang/chelis#3866 ·
I maintainer di solito rispondono entro 1 giorno
-
[安卓Feature]: 数字键盘左侧符号区域大小优化Apertaenhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
metasequoiaime/msime#8620 ·
I maintainer di solito rispondono entro 1 giorno
-
agent-detection macos p2 triaged
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
I maintainer di solito rispondono entro 1 giorno