docs(adr): ADR-0009: Audience Scopes for Filtered Documentation Views
@aRustyDev 已经在做这个了。
开始于 2026年1月5日。
评估
这个 Issue 还没有评估数据。
描述
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
- 主要语言
- Rust
- 星标
- 0
- 派生
- 1
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
aRustyDev/mdbook-htmx 的其他 Issue
-
feat(ci): Implement cross-repo-validate compliance for docs sync可能重新可做 @aRustyDev 于 298 天前认领,目前没有进行中的 PR。 未关闭ci enhancement
aRustyDev/mdbook-htmx#58 · 1 条评论 · 已指派 1 人 ·
-
docs(htmx): Explain how HTMX works可能重新可做 @aRustyDev 于 300 天前认领,目前没有进行中的 PR。 未关闭
aRustyDev/mdbook-htmx#48 · 3 条评论 · 已指派 1 人 ·
-
docs(mdbook-htmx): Parent Issue for documentation可能重新可做 @aRustyDev 于 300 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#47 · 已指派 1 人 ·
-
docs(examples): Secure Self-Hosted Meilisearch via Cloudflare可能重新可做 @aRustyDev 于 300 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#46 · 已指派 1 人 ·
-
docs(examples): Kubernetes with Meilisearch可能重新可做 @aRustyDev 于 300 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#45 · 已指派 1 人 ·
查看 aRustyDev/mdbook-htmx 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
longbridge/nsh#804 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
devex good first issue
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
calcit-lang/calcit#2538 ·
维护者通常 1 天内回复