Skip to content

docs: add Multiple Applications on One Cluster guide - #586

Merged
Ethan-Arrowood merged 9 commits into
mainfrom
docs/multiple-applications-one-cluster
Aug 31, 2026
Merged

docs: add Multiple Applications on One Cluster guide#586
Ethan-Arrowood merged 9 commits into
mainfrom
docs/multiple-applications-one-cluster

Conversation

@BboyAkers

@BboyAkers BboyAkers commented Jul 15, 2026

Copy link
Copy Markdown
Member

Issue: #587

Summary

Adds a new developer guide, Multiple Applications on One Cluster, covering how to run multiple applications on a single Harper cluster:

  • Applications as components on a shared cluster
  • Structuring each application with config.yaml
  • Keeping applications separate along three boundaries: routes, data, and roles
  • Registering applications declaratively (harperdb-config.yaml) and imperatively (CLI)
  • Deploying across the cluster with replicated=true
  • Guidance on when to co-locate vs. use separate clusters

Written in the platform's technical-documentation tone, consistent with the sibling harper-applications-in-depth.mdx guide.

🤖 Generated with Claude Code

Add a guide covering how to run multiple applications on a single Harper
cluster: how components coexist, how to isolate them via routes, data, and
roles, and how to deploy across every node.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BboyAkers
BboyAkers requested a review from a team as a code owner July 15, 2026 15:00

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a new documentation guide on running multiple applications on a single Harper cluster. The reviewer feedback correctly identifies several naming errors in the documentation, specifically pointing out that the configuration file should be named 'harper-config.yaml' instead of 'harperdb-config.yaml', and that the CLI command is 'harper' rather than 'harperdb'. Additionally, the reviewer suggested using a Docusaurus note admonition instead of a standard markdown blockquote for consistency.

Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
BboyAkers and others added 2 commits July 15, 2026 11:04
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Rename config file references to harper-config.yaml (was harperdb-config.yaml)
- Use the harper CLI consistently (was harperdb)
- Convert the restart note to a :::note admonition
- Fix broken doc links to resolve to /reference/v5/ targets

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pr-586 July 15, 2026 15:22 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together — the mechanics that are here (config.yaml precedence, ports, deploy_component replicated=true) are accurate, and the broken cross-references from the first pass are fixed.

Requesting changes on scope/completeness, though — for a "multiple applications on one cluster" guide, the routing story is the crux and it's currently thin:

Significant — routing is incomplete. The guide only covers static assets / Fastify. It doesn't explain middleware routing by URL path, which is the primary mechanism for fronting multiple apps on one instance (how a request path maps to a given application). That should be the backbone of this guide, not an aside.

Significant — virtual hosts are missing. Hostname-based routing (virtual hosts) is arguably the feature for hosting multiple apps on one cluster — routing by host, not just path. A multi-app guide that omits it leaves out the main pattern users will reach for.

Significant — module loader context isolation. When several apps run in one instance, how their module loader contexts are isolated (or shared) is a real correctness/operational concern and belongs in this guide — at minimum a section explaining the isolation model so users understand what is and isn't shared between co-located apps.

Happy to point at the canonical references for each of these if that helps. The foundation is good; it just needs the routing + isolation story filled in before it's the guide people should follow for this topic.

— review via KrAIs (Claude) on Kris's behalf

Address maintainer review requesting fuller routing and isolation coverage:

- Make routing the backbone: new "Routing requests to each application"
  section covering path-based routing (urlPath), the server.http()
  middleware chain, and host-based routing via request.host
- Add "What co-located applications share" section documenting the module
  loader context isolation model (isolated module caches, shared
  process-wide data/APIs, shared process lifecycle)
- Note SNI certificate configuration for serving multiple domains

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BboyAkers

Copy link
Copy Markdown
Member Author

Thanks @kriszyp — good call on scope. Pushed changes addressing all three:

  • Middleware routing is now the backbone. New top-level Routing requests to each application section with three parts: path-based routing (urlPath, including the ././ component-name prepend and the deploy_component urlPath override), the server.http() middleware chain (next(request) / runFirst), and host-based routing.
  • Host-based routing. Documented via a server.http() handler branching on request.host (routing admin.example.com vs listings.example.com), plus a pointer to SNI (tls array with per-domain host) for multi-domain certs. Note: I couldn't find a first-class virtual host config feature in the current docs, so I documented the middleware pattern using the documented request.host primitive. If there's a canonical virtual-host reference I missed, point me at it and I'll rewrite this section around it.
  • Module loader context isolation. New What co-located applications share section: module contexts isolated per app (Node VM loader / distinct module caches), while tables/databases and the Harper APIs are the same live, process-wide objects — and the process/restart lifecycle is shared. Sourced from the JavaScript Environment reference.

Verified with a full docusaurus build (no broken links/anchors) and Prettier.

@github-actions
github-actions Bot temporarily deployed to pr-586 July 15, 2026 18:30 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes, but narrower than the automated review suggested — checked the two API-shape claims against Harper's actual Request implementation before posting, and they don't hold up:

  • request.host and request.pathname are real, documented Harper Request properties (server/serverHelpers/Request.ts), not something that needs request.headers.get(...) instead — no change needed there.
  • Marking the handlers async isn't required for crash-safety either: requestHandler in server/http.ts already wraps the whole chain call in a try/catch (await httpChain[port](request)), which catches a synchronous throw from a non-async middleware just as reliably as an async one.

The one thing genuinely worth fixing: the first example's prose says "dispatch on a custom header" but the code shows a path-prefix check (request.pathname.startsWith('/listings')) — text and code don't match each other. Worth making the example match the text (a header check, since that's the case path-prefix routing doesn't already cover) so the doc isn't demonstrating the wrong tool for the job it describes. Not a correctness or performance bug, just an internal consistency fix.

The middleware-chain example described dispatching on a custom header but
showed a path-prefix check — a case path-based routing already covers.
Switch the example to a custom-header check so it demonstrates what the
surrounding text actually recommends the chain for.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BboyAkers

Copy link
Copy Markdown
Member Author

Thanks for checking the API shapes against the source, @kriszyp — agreed on all counts (request.host/request.pathname are real, and the chain's try/catch makes async unnecessary), so I left those as-is.

Fixed the one genuine issue: the middleware-chain example now dispatches on a custom header (x-app-target) to match its prose, instead of showing a path-prefix check that path-based routing already covers.

@github-actions
github-actions Bot temporarily deployed to pr-586 July 17, 2026 15:16 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the hostname-routing section. One significant correction is still needed: the current example manually branches on request.host, but Harper 5.2 has first-class virtual-host routing.

Components can declare host in config.yaml; the scoped server API automatically carries that value into the component’s HTTP, WebSocket, and upgrade registrations. Custom code can register the same routed chain directly with server.http(handler, { host: "admin.example.com" }). Harper then matches the request Host header and selects the corresponding isolated middleware chain.

Please make that declarative host configuration the primary application-routing pattern, show the direct { host } option as the programmatic equivalent, and reserve branching on request.host for genuinely custom dispatch. The SNI material should remain separate: SNI selects the certificate, while host selects the application middleware chain.

I opened Document middleware host and path routing (#595) to add the missing canonical references for both host and urlPath; this guide can link to those sections once it lands.

— review via KrAIs (GPT-5) on Kris’s behalf

Host-based routing led with manual request.host branching, but Harper
5.2 has first-class virtual-host routing: declaring host in a
component's config.yaml auto-scopes its whole middleware chain via the
scoped server API. Lead with that, show server.http({ host }) as the
programmatic equivalent, and reserve manual branching for dispatch that
needs runtime logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pr-586 July 24, 2026 17:28 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@BboyAkers
BboyAkers requested a review from kriszyp July 27, 2026 14:54

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update this for #612 ?

The overall guide is useful, but the host-routing example is configured at the wrong level and would leave the advertised application routes reachable on every host.
The data and authentication sections also describe per-application isolation that Harper does not enforce: databases are shared namespaces, and users/roles are instance-wide.

🤖 Reviewed with Codex

Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
Comment thread learn/developers/multiple-applications.mdx Outdated
@BboyAkers
BboyAkers marked this pull request as draft August 28, 2026 17:27
BboyAkers and others added 2 commits August 31, 2026 10:52
Addresses the outstanding review on #586, updated for #612 (root-config
application mounts).

- Routing: make the root `harper-config.yaml` application entry the
  primary mount (`host`/`urlPath`), with `deploy_component` as the
  deploy-time equivalent and `server.http(handler, { host, urlPath })` as
  the programmatic one. Branching on `request.host` is now reserved for
  genuinely custom dispatch. Adds a v5.2.0 badge.
- Adds "What a mount does not do": a mount is not a resource namespace
  (exports land in one instance-wide registry, so duplicate resource
  names still conflict), and it does not host-constrain `fastifyRoutes`
  (a host-mounted app declaring them fails to load). Drops `fastifyRoutes`
  from the primary config example; plugin `urlPath` now positions a plugin
  within the app, composing with the mount.
- Data: renamed to "Namespacing data by database" and states plainly that
  a database is a namespace, not an enforced boundary — every co-located
  component can reach any database through the shared `databases` object,
  and buggy or untrusted code is not prevented from crossing it. Real
  isolation requires a separate instance or cluster.
- Roles: replaced the per-application enforcement claim with the shared
  model — every `roles.yaml` reconciles into the instance-wide role
  registry (same name = same role, last load wins), users and sessions are
  instance-wide. Recommends application-prefixed role names with
  permissions scoped to each application's database.
- Replaces the deprecated `runFirst` option in the middleware example with
  the `name`/`before`/`after` ordering guidance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BboyAkers

Copy link
Copy Markdown
Member Author

Rebased-forward (merged main) and pushed a revision addressing all four open threads, updated for #612 as requested — that merged after the last review round and moved application mounting from the component's config.yaml to the application's root-config entry, so the host-routing section was rewritten around that rather than by moving host under fastifyRoutes.

Routing (was configured at the wrong level)

  • The root harper-config.yaml application entry is now the primary mount: host + urlPath per application, with deploy_component host=… urlPath=… as the deploy-time equivalent and server.http(handler, { host, urlPath }) as the programmatic one. Branching on request.host is reserved for genuinely custom dispatch (resolving a target host at runtime).
  • New What a mount does not do section: a mount is not a resource namespace (exports land in one instance-wide registry, so duplicate exported names still conflict), and it does not host-constrain fastifyRoutes — those register as a global fallback outside the routed chain, a urlPath mount applies but a host mount does not, and Harper refuses to load a host-mounted app that declares them. fastifyRoutes is out of the primary config.yaml example; a plugin's own urlPath is now framed as positioning it within the app and composing with the mount.
  • <VersionBadge version="v5.2.0" /> on the mounting section, and SNI stays separate (certificate selection vs. middleware-chain selection).

Data (described isolation Harper does not enforce)

  • Section renamed Namespacing data by database: a database is a namespace, databases is process-wide, every co-located component can reach every database through it, and buggy or untrusted component code is not prevented from crossing. Co-located apps share one trust domain; security/compliance isolation needs a separate instance or cluster. When to co-locate now reflects that too.

Roles (described per-application enforcement)

  • Section renamed Access control is instance-wide: every roles.yaml reconciles into the instance's single role registry (create-or-overwrite), users and sessions are instance-wide. Guidance is application-prefixed role names (two apps declaring reader are one role, last load wins) with permissions scoped to each application's database, and an explicit note that per-app access rules mean different roles inside one shared RBAC system.

Also swapped the deprecated runFirst in the middleware example for the name/before/after ordering guidance. prettier --check and npm run build are clean (no broken links or anchors — the #595/#612 reference anchors resolve now that main is merged in).

@BboyAkers
BboyAkers marked this pull request as ready for review August 31, 2026 14:57
@github-actions
github-actions Bot temporarily deployed to pr-586 August 31, 2026 14:57 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@BboyAkers
BboyAkers requested a review from kriszyp August 31, 2026 14:58

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good
🤖 Reviewed with Codex

Comment thread learn/developers/multiple-applications.mdx Outdated
A route-only entry (no package) is valid and is how a payload-deployed
application gets mounted, so the declarative-registration section no
longer states that every entry points to a package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pr-586 August 31, 2026 17:22 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-586

This preview will update automatically when you push new commits.

@Ethan-Arrowood
Ethan-Arrowood merged commit 48f13df into main Aug 31, 2026
10 checks passed
@Ethan-Arrowood
Ethan-Arrowood deleted the docs/multiple-applications-one-cluster branch August 31, 2026 18:13
@github-actions

Copy link
Copy Markdown

🧹 Preview Cleanup

The preview deployment for this PR has been removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants