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:
      <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:
      <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.config.jsGitHub ↗
// 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)