OpenWork Den Organization Management System Architecture: Typed Contracts and Pure Functions

The OpenWork Den organization management system architecture is built on a centralized type layer and pure JavaScript parsing functions that transform loose JSON API responses into strongly-typed objects, while a single permission calculator exposes boolean access flags that bind directly to the UI.

The different-ai/openwork repository powers multi-tenant organization management through its Den core service. The Den architecture stores and exposes all organization-level data—members, invitations, roles, teams, API keys, SCIM, SSO, and branding—via a type-safe front-end module. Every contract, parser, and permission helper lives in a single, side-effect-free utility file that the React application consumes through a dedicated provider.

Core Data Model and Typed Contracts in OpenWork Den

All domain types are exported as plain TypeScript type aliases from ee/apps/den-web/app/(den)/_lib/den-org.ts. These are deliberately class-free so they can be imported by React components, server-side helpers, and unit tests without side effects.

The most important contracts include:

  • DenOrgSummary — Minimal org information for the org selector.
  • DenOrgMember — A user or pending invite embedded with a profile.
  • DenOrgInvitation — Pending email invite state.
  • DenOrgTeam — Logical member grouping, optionally SCIM-managed.
  • DenOrgRole — Custom role with a permission map.
  • DenCanonicalRole — The built-in union "owner" | "super-admin" | "admin" | "member".
  • DenOrgAccessFlags — Computed booleans that drive UI enable/disable decisions.
  • DenOrgApiKey — Metadata for programmatic access.
  • DenOrgScimConnection and DenOrgSsoConnection — External SCIM and SSO integration descriptors.
  • DenManagedBrandAsset — Uploaded logo or icon asset.
  • DenOrganizationMetadata — Free-form JSON blob for custom settings such as desktop version whitelists or required SSO enforcement.

Pure Parsing and Validation Functions

Because the Den API returns loosely-typed JSON, the module provides pure parsing functions that validate shape and coerce values before the data reaches the UI layer.

  • parseOrganizationMetadata(metadata: string | null) — Safely JSON.parses the metadata string and returns a typed object or null.
  • parseOrgListPayload(payload: unknown) — Converts the /v1/me/orgs response into { orgs, activeOrgId, activeOrgSlug }.
  • parseOrgContextPayload(payload: unknown) — Parses the full organization context including org, current member, members, invitations, roles, and teams.
  • parseOrgApiKeysPayload, parseOrgScimPayload, parseOrgSsoPayload — Dedicated parsers for API keys, SCIM, and SSO endpoints.
  • isAssignableOrgRole and canRefreshInvitationRole — Business-rule helpers that protect built-in roles from improper assignment.

The parsers rely on low-level type guards such as isRecord, asString, asBoolean, and asStringArray. By keeping parsing pure, components downstream can trust the resulting objects and avoid runtime any checks.

Permission Calculation with getOrgAccessFlags

The most frequently consumed function in the Den architecture is getOrgAccessFlags. It takes a raw role string, an isOwner boolean, and an optional array of role definitions, then returns a flat boolean map that the UI binds directly to buttons and menu items.

export function getOrgAccessFlags(
  roleValue: string,
  isOwner: boolean,
  _roleDefinitions: readonly DenOrgRole[] = []
): DenOrgAccessFlags {
  const canonicalRole = getHighestCanonicalRole(roleValue, isOwner);
  const resolvedIsOwner = canonicalRole === "owner";
  const isSuperAdmin = canonicalRole === "super-admin";
  const isAdminRole = canonicalRole === "admin";
  const isAdmin = resolvedIsOwner || isSuperAdmin || isAdminRole;
  const canManageSettings = resolvedIsOwner || isSuperAdmin;

  return {
    canonicalRole,
    isOwner: resolvedIsOwner,
    isSuperAdmin,
    isAdminRole,
    isMember: canonicalRole === "member",
    isAdmin,
    canViewSettings: isAdmin,
    canManageSettings,
    canManageSecurityConfiguration: canManageSettings,
    canInviteMembers: isAdmin,
    canCancelInvitations: isAdmin,
    canManageMembers: isAdmin,
    canRemoveMembers: isAdmin,
    canManageRoles: canManageSettings,
    canManageTeams: isAdmin,
    canManageApiKeys: canManageSettings,
    canManageScim: canManageSettings,
    canManageSso: canManageSettings,
    canTransferOwnership: resolvedIsOwner,
    canDeleteOrganization: resolvedIsOwner,
    canStartSeatCheckout: isAdmin,
  };
}

As implemented in different-ai/openwork, this same matrix powers both the Den UI and the back-end service checks. A component can call it directly:

import { getOrgAccessFlags } from '@/app/(den)/_lib/den-org';

const flags = getOrgAccessFlags(currentMember.role, currentMember.isOwner);

if (flags.canInviteMembers) {
  // Render "Invite Member" button
}

Route Helpers and Navigation

The front-end generates org-scoped URLs through a family of pure functions that prepend /dashboard and encode query parameters. These helpers are consumed throughout the Den web app, including in ee/apps/den-web/app/(den)/_providers/den-flow-provider.tsx.

export function getOrgDashboardRoute(_orgSlug?: string | null): string {
  return "/dashboard";
}
export function getMarketplaceOnboardingRoute(_orgSlug?: string | null): string {
  return `${getOrgDashboardRoute(_orgSlug)}/onboarding`;
}
export function getJoinOrgRoute(invitationId: string): string {
  return `/join-org?invite=${encodeURIComponent(invitationId)}`;
}
export function getWorkspaceClaimRoute(token: string): string {
  return `/workspace-claim?token=${encodeURIComponent(token)}`;
}

These utilities guarantee consistent navigation after sign-in, invitation acceptance, or workspace claim.

Brand Assets and Feature Flags

Each org can customize branding and behavior through the metadata JSON blob. Small accessor functions read this blob safely after it has been parsed:

  • getAllowedDesktopVersionsFromMetadata — Returns a deduplicated allow-list of desktop client versions.
  • getRequireSsoFromMetadata — Reads whether SSO enforcement is active.
  • getManagedBrandAssetFromMetadata — Extracts a stored logo or icon asset.
  • getManagedBrandIconUrl — Convenience wrapper that returns the icon URL directly.

A typical read looks like this:

import {
  parseOrganizationMetadata,
  getManagedBrandIconUrl,
} from '@/app/(den)/_lib/den-org';

const meta = parseOrganizationMetadata(org.metadata);
const brandIcon = meta ? getManagedBrandIconUrl(org.metadata) : null;

All metadata parsing lives alongside the core types in den-org.ts, keeping org-level concerns centralized.

End-to-End Data Flow in the OpenWork Den System

The Den organization management system architecture follows a predictable pipeline from authentication to rendering:

  1. Authentication — DenFlowProvider obtains a JWT and enters the org routing layer.
  2. Directory Load — loadOrgDirectory() calls /v1/me/orgs and feeds the raw response to parseOrgListPayload.
  3. Context Load — loadOrgContext() fetches /v1/orgs/:slug and runs parseOrgContextPayload to produce a DenOrgContext.
  4. Permission Resolution — getOrgAccessFlags derives the current user's boolean capability map.
  5. UI Binding — Components consume typed data directly, e.g., <MemberList members={ctx.members} />, with no additional runtime validation.
  6. Navigation — Route helpers generate consistent, org-scoped links for deep pages.

For production, the Den service is packaged via the Den web and API Helm charts located in dev/packaging/helm/openwork-ee/templates/den-api.yaml and templates/den-web.yaml.

Unit tests in den-org.brand-icon.test.mts and den-org.selection.test.mts guard the parsing utilities, while integration tests in dev/apps/app/tests/den-web-org-access.test.ts validate the full auth-to-dashboard flow.

Summary

The OpenWork Den organization management system architecture is founded on a type-first, pure-function design:

  • Typed contracts in den-org.ts keep the codebase safe and IDE-friendly.
  • Pure parsers validate and coerce raw JSON into trusted objects.
  • getOrgAccessFlags exposes a flat boolean map for direct UI binding.
  • Route generators create consistent, org-scoped dashboard URLs.
  • Metadata accessors read custom branding and feature flags without leaking implementation details.

This centralized module is wired into the React front-end through DenFlowProvider, making it straightforward to extend org-level settings without touching unrelated UI code.

Frequently Asked Questions

What file contains the core types and parsers for the OpenWork Den architecture?

All core types, parsing functions, permission helpers, and route builders live in ee/apps/den-web/app/(den)/_lib/den-org.ts. This single module is imported by React components, server-side helpers, and unit tests across the Den web app.

How does the Den system calculate what an organization member is allowed to do?

The system calls getOrgAccessFlags(roleValue, isOwner, roleDefinitions), which resolves the highest canonical role and returns an object of booleans such as canInviteMembers, canDeleteOrganization, and canManageSso. Both the UI and the back-end rely on this same function to enforce access control consistently.

Where does the organization data flow begin after a user signs in?

After sign-in, the DenFlowProvider in ee/apps/den-web/app/(den)/_providers/den-flow-provider.tsx manages the JWT and triggers loadOrgDirectory(). That helper calls /v1/me/orgs and uses parseOrgListPayload to populate the org selector or auto-select the active organization.

How are custom branding and feature flags stored in the Den system?

They are stored in a free-form metadata JSON field on the organization object. Helper functions such as parseOrganizationMetadata, getManagedBrandIconUrl, and getRequireSsoFromMetadata safely extract values from this blob, allowing per-org logos, icon assets, and SSO enforcement without schema migrations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →