Skip to main content
Jira progress: loading…

Audience configuration and lifecycle

This document explains how to add or update a documentation audience for ZAYAZ Docs (e.g. client, developer, internal, or a new one like partner), and how those audiences connect to:

  • Docusaurus (conditional content via AudienceBlock)
  • Cloudflare Pages (separate deployments per audience)
  • Cloudflare Access (optional password / SSO protection)
  • The signed manifest.json (per-audience build metadata)

An audience in this setup affects:

  • What content is visible in MDX via <AudienceBlock />
  • Which Cloudflare Pages project / domain serves the docs
  • Who can access it (public vs password / SSO via Cloudflare Access)
  • Which manifest.json the MCP uses for that audience

Typical audiences today:

  • client – external customers (public or lightly protected)
  • developer – engineers, vendors, technical partners
  • internal – EcoWorld/ZAYAZ core team

You can extend this with new audiences like partner, auditor, etc., following this guide.


1. High-level architecture

Each audience is the combination of:

  1. Docusaurus audience flag

    • Configured in docusaurus/docusaurus.config.js via environment variable DOCS_AUDIENCE.
    • Exposed in the site as siteConfig.customFields.audience.
  2. Audience-aware components in MDX

    • AudienceBlock reads siteConfig.customFields.audience and hides/shows content.
    • Example in an .mdx file:
      ```mdx
      <AudienceBlock audience={['internal','developer']}>
      Internal + developer-only content
      </AudienceBlock>
  3. Cloudflare Pages projects

    • Each audience has its own Pages project:
      • zayaz-docs-client
      • zayaz-docs-developer
      • zayaz-docs-internal
    • All projects point to the same GitHub repo/branch but run with different environment variables.
  4. Cloudflare Access (optional)

    • Public audiences (for example client) can be left without Access protection.
    • Internal audiences use Cloudflare Access with SSO, email-based OTP, or group-based rules.
    • Access configuration is documented in security/cloudflare-access-mapping.md.
  5. Signed manifest

    • After a build, we generate manifest.json for that audience and sign it with GPG.
    • This allows external verifiers or MCP to confirm which pages exist for which audience and when they were built.

2. Files and directories involved

When you add or modify an audience, you will typically touch:

  • docusaurus/docusaurus.config.js
    Uses process.env.DOCS_AUDIENCE and exposes customFields.audience.

  • .github/workflows/portal.yml (or similar CI workflow)

    • Sets DOCS_AUDIENCE for the build.
    • Calls the Docusaurus build.
    • Runs manifest generation + signing.
    • Deploys to the correct Cloudflare Pages project.
  • docusaurus/src/components/AudienceBlock.js

    • Implements audience-based filtering of MDX content.
  • MDX content files (for example content/computation-hub/mice-micro-engines/pef-me.mdx)

    • Use <AudienceBlock audience={['...']}> where appropriate.
  • Cloudflare Pages UI

    • One Pages project per audience.
    • Environment variables and domains are configured in the dashboard.
  • Cloudflare Zero Trust / Access UI

    • Optional access rules and “password-like” protection for non-public audiences.
  • security/cloudflare-access-mapping.md

    • Documents which routes / hostnames are protected by which Access policies.

3. Quick checklist for adding a new audience

Assume the new audience is called partner.

  1. Choose an audience ID

    • Use a simple, lowercase string: partner.
    • This ID will be used by:
      • DOCS_AUDIENCE env var
      • customFields.audience
      • AudienceBlock usage in MDX
  2. Create a new Cloudflare Pages project

    • Example project name: zayaz-docs-partner.
    • Point it to the same GitHub repo and branch.
    • Build command (example):
      • npm run build:partner
    • Output directory: docusaurus/build (or just build if you build from inside /docusaurus).
  3. Add a CI job for the new audience

    • In .github/workflows/portal.yml, add a job (or matrix entry) that:
      • Sets DOCS_AUDIENCE=partner.
      • Builds the docs.
      • Runs generate-manifest.js with --audience partner.
      • Signs manifest.json.
      • Deploys to zayaz-docs-partner using Cloudflare API (using secrets).
  4. Configure secrets in GitHub

    • Reuse:
      • CF_API_TOKEN
      • CF_ACCOUNT_ID
    • Add:
      • CF_PAGES_PROJECT_PARTNER (the exact project name in Cloudflare Pages)
    • If signing is enabled (recommended):
      • GPG_PRIVATE_KEY
      • GPG_PASSPHRASE
  5. Configure Cloudflare Access (optional)

    • If the partner docs should be protected:
      • Create a Cloudflare Access application for the partner hostname (for example partner-docs.zayaz.io).
      • Add rules:
        • For example: emails ending with @partnercompany.com, or a one-time passcode mechanism.
    • If the docs should be public:
      • Do not attach an Access app, or configure a rule that allows public access.
  6. Use the audience in MDX

    • Wherever you want partner-only content:
      ```mdx
      <AudienceBlock audience={['partner']}>
      Partner-only content goes here.
      </AudienceBlock>
  7. Update documentation

    • Add an entry to security/cloudflare-access-mapping.md (or another security README) describing:
      • Hostname for partner docs.
      • Which Access policy applies.
      • How to request access.

4. Docusaurus audience configuration

The audience is wired into Docusaurus via customFields.audience.

Example docusaurus/docusaurus.config.js fragment:

// docusaurus/docusaurus.config.js

// @ts-check
import {themes as prismThemes} from 'prism-react-renderer';

/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'ZAYAZ Docs',
url: 'https://docs.zayaz.io',
baseUrl: '/',
favicon: 'img/favicon.ico',
i18n: { defaultLocale: 'en', locales: ['en'] },

// Audience injected via environment at build time
customFields: {
audience: process.env.DOCS_AUDIENCE || 'internal',
},

presets: [
[
'classic',
({
docs: {
path: '../content',
routeBasePath: '/',
editUrl: undefined,
showLastUpdateTime: true,
showLastUpdateAuthor: true,
include: ['**/*.md','**/*.mdx'],
},
blog: false,
theme: { customCss: require.resolve('./src/css/custom.css') },
})
]
],

themeConfig: {
navbar: {
title: 'ZAYAZ Docs',
items: [
{href: '/', label: 'Home', position: 'left'}
],
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ['python','bash','yaml','json','sql']
}
}
};
export default config;
<AudienceBlock audience={['client']}>
This section is visible only in the client docs build.
</AudienceBlock>

5.2 Multiple audiences

<AudienceBlock audience={['internal', 'developer']}>
This section is visible in both internal and developer builds.
</AudienceBlock>

5.3 Nested audiences

You can nest blocks to progressively narrow visibility:

<AudienceBlock audience={['internal', 'developer']}>
### Internal Notes

<AudienceBlock audience={['developer']}>
Developer-only Deep Dive
</AudienceBlock>

</AudienceBlock>

6. CI workflow: building and signing per audience

Below is an illustrative fragment of a GitHub Actions workflow that:

  • Generates registry JSON files
  • Copies Excel assets
  • Builds Docusaurus for a given audience
  • Generates manifest.json
  • Signs it with GPG
# .github/workflows/portal.yml (excerpt)

name: Build and deploy docs

on:
push:
branches: [ main ]

jobs:
build-and-deploy-client:
runs-on: ubuntu-latest
env:
DOCS_AUDIENCE: client
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '22'

# Convert registries → JSON
- name: Generate Registry JSON
run: node scripts/generate-registry-json.js

- name: Generate Excel files JSON
run: node scripts/generate-excel-files-json.js

- name: Generate Signal Registry JSON
run: node scripts/generate-signal-registry-json.js

# Copy Excel files to Docusaurus static site
- name: Copy Excel files
run: |
mkdir -p docusaurus/static/excel
cp excel/*.xlsx docusaurus/static/excel/

# Build portal
- name: Build Docusaurus
run: |
cd docusaurus
npm ci
npm run build

# Generate manifest for this audience
- name: Generate manifest.json
run: |
node scripts/generate-manifest.js --audience client --buildDir build

# Install GnuPG
- name: Install GnuPG
run: sudo apt-get update && sudo apt-get install -y gnupg

# Import signing key
- name: Import signing key
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
gpg --list-keys

# Sign and checksum manifest
- name: Sign and checksum manifest
env:
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
cd docusaurus/build
sha256sum manifest.json > manifest.json.sha256
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
-o manifest.json.sig --detach-sign manifest.json

# Deploy to Cloudflare Pages
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
projectName: ${{ secrets.CF_PAGES_PROJECT_CLIENT }}
directory: docusaurus/build

To add a new audience, you can:

  • Clone this job as build-and-deploy-partner.
  • Change:
  • DOCS_AUDIENCE: partner
  • --audience partner in the manifest step
  • CF_PAGES_PROJECT_PARTNER in the deploy step

7. Password / access control per audience

Cloudflare Access is where you define “password-like” protection.

Typical patterns:

  • client:
    • Either public (no Access) or simple email-based OTP if you want light gating.
  • developer:
    • Restricted to specific email domains, GitHub org, or Okta/AAD groups.
  • internal:
    • Restricted to EcoWorld/ZAYAZ employees only.

Document the mapping in security/cloudflare-access-mapping.md, for example:

## Access mapping

- Audience: client
- Hostname: docs.zayaz.io
- Cloudflare Access: none (public)

- Audience: developer
- Hostname: dev-docs.zayaz.io
- Cloudflare Access: App "ZAYAZ Dev Docs"
- Rules:
- Identity provider: GitHub / Google Workspace
- Allowed groups: `zayaz-devs`, `trusted-vendors`

- Audience: internal
- Hostname: internal-docs.zayaz.io
- Cloudflare Access: App "ZAYAZ Internal Docs"
- Rules:
- Email domain: `@zayaz.io`, `@ecoworld.ai`

If you want something closer to a shared password, you can:

  • Use “Service Auth” tokens or
  • Configure a group in your IdP and only give group membership to people you share the “password” with.
  • Use Cloudflare.com's Zero Trust access setup.

8. Updating an existing audience

When you update an audience (for example, change who can see it or how content is segmented):

  1. Content changes
  • Update MDX files to move content in/out of <AudienceBlock> wrappers.
  • Use audience tags consistently:
<AudienceBlock audience={['client','partner']}>
Shared external description.
</AudienceBlock>
  1. Access changes
  • Update Cloudflare Access rules.
  • Update security/cloudflare-access-mapping.md to reflect the new rules.
  1. Deployment changes
  • If hostnames or Cloudflare Pages project names change:
  • Update GitHub Action secrets (CF_PAGES_PROJECT_...).
  • Update DNS records in Cloudflare.
  1. Validation
  • Trigger a new build (push to main).
  • Visit all audience URLs and confirm:
  • Content visibility behaves as expected.
  • Access rules behave as expected.
  • manifest.json and manifest.json.sig exist in the built output.

9. Removing an audience

To decommission an audience:

  1. Remove its job from .github/workflows/portal.yml.
  2. Optionally remove the Cloudflare Pages project.
  3. Remove or update any MDX content that references that audience.
  4. Update security/cloudflare-access-mapping.md and any architectural diagrams.
  5. Remove related secrets from GitHub if no longer needed.

10. Summary

To add or change an audience, always think in terms of four layers:

  1. Build flag DOCS_AUDIENCE → customFields.audience.
  2. Content <AudienceBlock audience={['...']}> in MDX.
  3. Deployment Cloudflare Pages project + GitHub Action job.
  4. Access Cloudflare Access policies + documented mapping.

Follow the checklist in section 3 each time you introduce or significantly change an audience

GitHub RepoRequest for Change (RFC)