docs(adr): ADR-0009: Audience Scopes for Filtered Documentation Views

未关闭
#17 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

@aRustyDev 已经在做这个了。

开始于 2026年1月5日。

评估

这个 Issue 还没有评估数据。

描述

documentation

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

  1. Focus - Users shouldn't wade through irrelevant content
  2. Security - Users shouldn't see content they're not authorized for
  3. Simplicity - Minimal configuration burden on content authors
  4. 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:

  • scopes controls navigation filtering (user's choice)
  • auth controls 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 developers but require role senior-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 all catches 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 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

aRustyDev/mdbook-htmx 的其他 Issue

查看 aRustyDev/mdbook-htmx 的全部 Issue

相似的 Issue

更多 Rust Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。