diff --git a/.github/workflows/v0-behavior.yml b/.github/workflows/v0-behavior.yml
new file mode 100644
index 00000000..2d9cd2ff
--- /dev/null
+++ b/.github/workflows/v0-behavior.yml
@@ -0,0 +1,25 @@
+name: V0 behavior checks
+on:
+ pull_request:
+ workflow_dispatch:
+permissions:
+ contents: read
+jobs:
+ behavior:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: '22'
+ cache: npm
+ - name: Install locked dependencies without lifecycle hooks
+ run: npm ci --ignore-scripts --legacy-peer-deps --no-audit --no-fund
+ - name: Run focused behavior and HTTP routing tests
+ run: npm run test:v0
+ - name: Generate local environment modules without credentials
+ env:
+ CI: 'false'
+ run: node tools/set-env
+ - name: Check application types and templates
+ run: node_modules/.bin/ngc --noEmit -p apps/36-blocks/tsconfig.app.json
diff --git a/AGENTS.md b/AGENTS.md
index 5c4bd672..7db2e24d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,75 +1,11 @@
-# proxy-ui (36 Blocks frontend)
+# Working on proxy-ui
-An Nx monorepo (Angular 21, standalone components, NgRx, Tailwind v4) with two apps:
-- `apps/36-blocks`: the customer dashboard and the marketing site.
-- `apps/36-blocks-widget`: an embeddable auth widget, built into one script (proxy-auth.js) that customer sites load and call through `window.initVerification(config)`.
+Owns the Angular public website, operator dashboard and embeddable authentication widget. Backend APIs, persistence and session policy belong to sibling `user-proxy`; connected applications own business authorization.
-Both apps are client-only and call a backend "proxy" API that lives in another repo.
+Start with the canonical [work state](../user-proxy/docs/WORK-STATE.md), [architecture](../user-proxy/docs/ARCHITECTURE.md), [capabilities](../user-proxy/docs/CAPABILITIES.md), [development](../user-proxy/docs/DEVELOPMENT.md), [security](../user-proxy/docs/SECURITY-OPERATIONS.md) and [design](../user-proxy/docs/DESIGN.md). Preserve existing README contribution rules and DESIGNGUIDE.md. Read widget integration/event guides before changing the custom element contract.
-## Commands
-
-| Task | Command |
-|---|---|
-| Install | `npm ci --legacy-peer-deps` (Node 20; peer deps conflict without the flag) |
-| Env setup | `cp .env.example .env` and fill in the values. `tools/set-env.js` generates the git-ignored env-variables.ts for each app ([docs/environment.md](docs/environment.md)) |
-| Run dashboard | `npm start` (port 4200) |
-| Run widget alone | `npm run start:proxy-auth` (also port 4200) |
-| Build widget into dashboard assets | `npm run build:proxy-auth` |
-| Full build | `npm run build:test`, `npm run build:stage` or `npm run build:prod`. Plain `npm run build` mixes a prod widget with a local dashboard |
-| Format | `npm run format:code`. Prettier also runs on pre-commit through husky and lint-staged |
-
-`npm test` and `npm run lint` are not reliable at this commit: there is no test target on the dashboard, and `nx workspace-lint` is gone in Nx 21. Check a change by building the affected app: `npx nx build 36-blocks` or `npx nx build 36-blocks-widget` (run `node tools/set-env` first).
-
-## Read before you change
-
-- Anything → [docs/architecture.md](docs/architecture.md), then [docs/conventions.md](docs/conventions.md)
-- The widget's public API, events or build → [docs/modules/widget-embed.md](docs/modules/widget-embed.md) and [docs/api.md](docs/api.md#widget-public-api-host-page--widget)
-- Widget login, register or OTP → [docs/modules/widget-auth.md](docs/modules/widget-auth.md), [docs/flows/widget-verification.md](docs/flows/widget-verification.md)
-- Widget account screens → [docs/modules/widget-account.md](docs/modules/widget-account.md)
-- Dashboard login, token or guards → [docs/modules/app-shell.md](docs/modules/app-shell.md), [docs/flows/dashboard-login.md](docs/flows/dashboard-login.md)
-- Blocks (features) wizard → [docs/modules/panel-features.md](docs/modules/panel-features.md), [docs/flows/feature-configuration.md](docs/flows/feature-configuration.md)
-- Users, logs, billing, dashboard, layout → [docs/modules/panel-ops.md](docs/modules/panel-ops.md)
-- `libs/**`, HTTP, the interceptor → [docs/modules/shared-libs.md](docs/modules/shared-libs.md)
-- Routes, state, components, styling → [docs/ui/](docs/README.md#ui)
-- A common change (page, backend call, block tab, widget key or screen, env value, lib, release) → [docs/recipes.md](docs/recipes.md)
-
-## Rules
-
-1. Never rename or remove any part of the widget's public contract:
- - `window.initVerification` and the `proxy-auth` tag
- - `window.__proxyAuth` and the `openAddUserDialog` event
- - the `userProxyContainer` id and the assets/proxy-auth/proxy-auth.js path
- - any value in `libs/constant/src/verification/verification.ts`
-
- Customer sites depend on all of these. Add new values instead ([docs/api.md](docs/api.md)).
-2. No dynamic `import()` in `apps/36-blocks-widget`. `build-elements.js` leaves the `chunk-*` file it creates out of the single-file bundle, so the bundle breaks on host pages.
-3. A new `initVerification` config key that must not be sent to the backend goes into `RESERVED_KEYS` in `apps/36-blocks-widget/src/app/init-verification.ts`. Every other key is POSTed as `otherData` ([recipe](docs/recipes.md#add-an-initverification-config-key)).
-4. Put widget CSS in component styles. `apps/36-blocks-widget/src/styles.scss` is also injected into the host page's `
`. Build overlays with `WidgetPortalService` / `WidgetDialogService`.
-5. Set and clear the dashboard token only through `AuthService.setTokenSync` / `clearTokenSync`. They keep the `authToken` cookie and the in-memory `IToken` in sync. Guards read both, HttpWrapper reads memory, and ErrorInterceptor reads only the cookie ([docs/modules/app-shell.md](docs/modules/app-shell.md)).
-6. A dashboard endpoint called without a session must be on the ErrorInterceptor allow-list. Otherwise it is silently dropped (`EMPTY`) ([docs/modules/shared-libs.md](docs/modules/shared-libs.md)).
-7. A 200 can still be an error. Check `res.hasError` (dashboard) or the per-endpoint success field (widget) as well as using `catchError` ([docs/conventions.md](docs/conventions.md#http-and-errors)).
-8. A block has two ids: numeric `id` for feature CRUD, OAuth and scopes, and string `reference_id` for the widget, billing and the Users/roles screens. Passing the wrong one fails silently ([docs/glossary.md](docs/glossary.md)).
-9. In the widget, copy `OtpService.options` before adding per-call headers. It is one shared mutable object, and its headers leak into later calls.
-10. The widget must not import `@proxy/services/proxy/*`, `@proxy/services/httpWrapper` or the login service. They need `AngularFireAuth` and `IToken`, which only the dashboard provides.
-11. Add dashboard pages to `apps/36-blocks/src/app/panel/panel.routes.ts`. `apps/36-blocks/src/app/panel/layout/layout.routes.ts` is dead (nothing imports it).
-12. Never send a client id. The current client is stored on the server against the token, and switching client is `POST switchclient` followed by a full page reload.
-13. Everything `tools/set-env.js` writes ends up in a public browser bundle. Never add a real server-side secret there. When you add a key, also add it to `.env.example`.
-14. Keep the `setTimeout(…, 100)` before block create/update in `create-feature.component.ts`. The payload is filled in by promises (file reads, chip lists) ([docs/modules/panel-features.md](docs/modules/panel-features.md)).
-15. Branch flow is `-clean` → `stage` → `production`; the README's `development`/`main` flow is stale. PRs into `stage` get **no CI build**, so build locally first. Land any revert on `stage` too ([docs/conventions.md](docs/conventions.md#branching-and-release)).
-
-## Where things are
-
-- `apps/36-blocks/src/app/website/`: public marketing pages, login and register
-- `apps/36-blocks/src/app/panel/`: the logged-in `/app` dashboard pages, each with a `.store.ts` ComponentStore
-- `apps/36-blocks/src/app/core/`: the root NgRx store, the auth initializer, client registration
-- `apps/36-blocks-widget/src/app/init-verification.ts`: the widget's global API
-- `apps/36-blocks-widget/src/app/otp/`: widget screens, the `otp` store, `OtpService`
-- `apps/36-blocks-widget/build-elements.js`: post-build step that produces the single proxy-auth.js
-- `libs/`: shared code, imported only through the `@proxy/...` aliases in `tsconfig.base.json`
-- `tools/set-env.js`, `tools/postbuild.js`: env generation and the version.json writer
-- `apps/shared/scss/`: global SCSS and Tailwind entry shared by both apps
-
-## Docs map
-
-See [docs/README.md](docs/README.md) for every doc, when to read it, and the open questions for humans.
-When you change behaviour that a doc describes, update that doc in the same change and run `/ai-docs update`.
+- Inspect status/diff; preserve unrelated changes. New application code uses TypeScript and existing standalone Angular patterns. Use Angular CLI scaffolding when practical; never change architecture solely to satisfy scaffolding.
+- Install from lock using `npm ci --ignore-scripts --legacy-peer-deps`; generate ignored env modules with `node tools/set-env`. Review environment base URLs before starting: local config defaults to a remote test API.
+- Run `node --test tools/tests/*.test.cjs`, affected builds and `git diff --check`. Existing Nx/Jest targets have known gaps: report exact commands and limitations in canonical work state.
+- Keep error, denied, empty and loading states distinct; provide retry. Never render request data as HTML or log/store tokens. Client visibility does not replace server authorization.
+- Update the single backend-owned capability matrix and work state alongside changes. Product/architecture guidance is linked, not duplicated here. Public claims require evidence; do not invent SLAs, guarantees or future functional controls.
diff --git a/DESIGNGUIDE.md b/DESIGNGUIDE.md
index f9a3dde3..811df2d1 100644
--- a/DESIGNGUIDE.md
+++ b/DESIGNGUIDE.md
@@ -1,20 +1,8 @@
# Proxy UI Design Guide
-## Setup Guide
+## Setup
-1. Clone https://github.com/Walkover-Web-Solution/proxy-UI.git.
-
-2. This project requires Node version 14+, install Node from [here](https://nodejs.org/en/).
-
-3. Go inside the cloned directory `proxy-UI` in any terminal and run these following commands in the terminal to install dependency and start local serve.
-
-4. Run `npm i` for installation of dependencies.
-
-5. Serving the project locally:
- * For proxy: Run `npm start` to local serve the project.
- * For Admin Panel: Run `npm run start:admin` to local serve the project.
- * For chat-widget: Run `npm run start:chat-widget` to local serve the project.
- * For otp-provider: Run `npm run start:otp-provider` to local serve the project.
+Use the current [README](README.md) and canonical [development guide](../user-proxy/docs/DEVELOPMENT.md) for runtime, install and startup commands. Legacy admin/chat/OTP application commands formerly listed here no longer describe the supplied workspace. The following styling guidance remains reference material; use the canonical [design standards](../user-proxy/docs/DESIGN.md) for product behavior.
## Prerequisites
It is important to have a thorough understanding of **HTML**, **CSS** and its pre-processor **SASS** to contribute to this project. If unaware of any of the above then go through their documention first.
diff --git a/README.md b/README.md
index ccd8dd09..535ca807 100755
--- a/README.md
+++ b/README.md
@@ -6,13 +6,17 @@
1. Clone https://github.com/Walkover-Web-Solution/proxy-ui.git.
-2. This project requires Node version 14+, install Node from [here](https://nodejs.org/en/).
+2. This is Angular 21.2 / Nx 21.6 / TypeScript 5.9. Use a compatible Node runtime; see the [canonical development guide](../user-proxy/docs/DEVELOPMENT.md). Node 14 is unsupported.
3. Go inside the cloned directory `proxy-ui` in any terminal and run these following commands in the terminal to install dependency and start local serve.
-4. Run `npm i` for installation of dependencies.
+4. Run `npm ci --ignore-scripts --legacy-peer-deps` to install the locked dependencies. Review the development guide before enabling install hooks.
-5. Run `npm start` to local serve the project.
+5. Configure approved local environment values, run `node tools/set-env`, then `npm start`. The checked-in local API URL points to a remote test service; change it before exercising mutations.
+
+Read [AGENTS.md](AGENTS.md), [current work state](../user-proxy/docs/WORK-STATE.md), [architecture](../user-proxy/docs/ARCHITECTURE.md) and the authoritative [capability matrix](../user-proxy/docs/CAPABILITIES.md). The API is the sibling `user-proxy` repository. `node --test tools/tests/*.test.cjs` runs focused behavior tests; legacy Nx/Jest targets have known gaps recorded in the work state.
+
+The existing contribution and style guidance below remains applicable. Use the canonical development and design guides where obsolete runtime/module instructions conflict with the current standalone Angular architecture.
## Contributing Guide
@@ -344,3 +348,9 @@ We have a server [Marlin](marlin.phone91.com) [for Prod] & [sailfish](sailfish.p
If client origin is widget (origin = `chat`) [will implement for all in v2], we have an option to start meet from `client-card-component`.
For selected client we use `call-session-creds` API to fetch `room` & `jwt` to init socket connection and Jitsi meet room with Jitsi Options. [Jitsi Reference Doc](https://developer.8x8.com/jaas/docs/iframe-api-integration)
+
+## Production integration — 29 October 2026
+
+The V0 changes are reconciled with production commit `2b1deb17`. Production deliberately uses a browser-only build and removed `ProjectGuard`; this merge preserves both decisions and the newer production navigation, OAuth/client-secret features, and compact website spacing. The original feature branch's SSR and project-guard changes/tests are therefore not part of this production integration.
+
+Validation: 23 applicable `npm run test:v0` checks, Angular type/template checking, and `npm run build:prod` passed, including the widget bundle and post-build packaging. Build warnings remain; this does not certify configured Firebase/provider login. Activity and Login as require the companion backend API/migrations from [user-proxy PR #1476](https://github.com/Walkover-Web-Solution/user-proxy/pull/1476) before live use. See the canonical [work state](../user-proxy/docs/WORK-STATE.md) for remaining integration gates.
diff --git a/apps/36-blocks/src/app/core/ngrx/effects/root/root.effect.ts b/apps/36-blocks/src/app/core/ngrx/effects/root/root.effect.ts
index f75e4a5d..8e86fe2e 100755
--- a/apps/36-blocks/src/app/core/ngrx/effects/root/root.effect.ts
+++ b/apps/36-blocks/src/app/core/ngrx/effects/root/root.effect.ts
@@ -82,7 +82,7 @@ export class RootEffects {
switchMap((req) => {
return this.rootService.getProjects().pipe(
map((res) => {
- if (res.hasError) {
+ if (res.hasError || !Array.isArray(res.data?.data)) {
this.showError(res.errors);
return rootActions.getAllProjectsError();
}
diff --git a/apps/36-blocks/src/app/core/ngrx/store/reducer/root.reducer.ts b/apps/36-blocks/src/app/core/ngrx/store/reducer/root.reducer.ts
index 6445d5d8..d8e4b2ae 100755
--- a/apps/36-blocks/src/app/core/ngrx/store/reducer/root.reducer.ts
+++ b/apps/36-blocks/src/app/core/ngrx/store/reducer/root.reducer.ts
@@ -18,6 +18,7 @@ export interface IRootState {
//All project
allProjects: IPaginatedResponse;
projectInProcess: boolean;
+ projectLoadFailed: boolean;
}
export const initialState: IRootState = {
@@ -33,6 +34,7 @@ export const initialState: IRootState = {
clientsInProcess: false,
switchClientSuccess: false,
projectInProcess: false,
+ projectLoadFailed: false,
allProjects: null,
};
@@ -121,6 +123,7 @@ const _rootReducer = createReducer(
return {
...state,
projectInProcess: true,
+ projectLoadFailed: false,
};
}),
on(rootActions.getAllProjectSuccess, (state, { response }) => {
@@ -128,6 +131,7 @@ const _rootReducer = createReducer(
...state,
allProjects: response,
projectInProcess: false,
+ projectLoadFailed: false,
};
}),
on(rootActions.getAllProjectsError, (state) => {
@@ -135,6 +139,7 @@ const _rootReducer = createReducer(
...state,
allProjects: null,
projectInProcess: false,
+ projectLoadFailed: true,
};
})
);
diff --git a/apps/36-blocks/src/app/core/ngrx/store/selectors/root.selector.ts b/apps/36-blocks/src/app/core/ngrx/store/selectors/root.selector.ts
index c3ccb551..3a3e3efe 100755
--- a/apps/36-blocks/src/app/core/ngrx/store/selectors/root.selector.ts
+++ b/apps/36-blocks/src/app/core/ngrx/store/selectors/root.selector.ts
@@ -32,3 +32,10 @@ export const selectProjectInProcess = createSelector(
selectRootState,
(rootState: IRootState) => rootState.projectInProcess
);
+
+/** One selector keeps project data and its request outcome consistent for guards. */
+export const selectProjectAccessState = createSelector(selectRootState, (state: IRootState) => ({
+ projects: state.allProjects,
+ loading: state.projectInProcess,
+ failed: state.projectLoadFailed,
+}));
diff --git a/apps/36-blocks/src/app/panel/activity/activity.component.html b/apps/36-blocks/src/app/panel/activity/activity.component.html
new file mode 100644
index 00000000..48035377
--- /dev/null
+++ b/apps/36-blocks/src/app/panel/activity/activity.component.html
@@ -0,0 +1,225 @@
+
+
+
Activity
+
+ Investigate operator actions for the current client. Login as records include the actor, target, reason and
+ outcome.
+
+
+ Older operations may have no audit details. A started attempt without a final outcome needs investigation.
+
+
+
+
+ Login as an end user
+
+ For client Owners, Admins and Super admins. This creates a session with the target user's access to their
+ application. The action and your reason are recorded in Activity. Use internal numeric IDs from the current
+ client. If no company is specified, the user's first company is checked for eligibility.
+
+
+ Confirm the user and company before continuing. Configure an HTTPS application redirect on the block before
+ starting; this panel does not display raw session tokens. The application remains responsible for business
+ permissions.
+
36Blocks was created to remove the burden of building authentication from scratch. We believe every
- developer deserves a production-ready, secure auth stack they can drop in within minutes — not months.
+ developer should be able to configure authentication, understand failures, and enforce application access
+ with clear boundaries.
@@ -98,7 +99,9 @@
What We Bui
Ready to get started?
-
Ship auth in 5 minutes. Free forever on the Starter plan.
+
+ Explore the configured capabilities and read the integration guide before connecting your application.
+
+
+
diff --git a/apps/36-blocks/src/app/website/docs/docs.component.ts b/apps/36-blocks/src/app/website/docs/docs.component.ts
new file mode 100644
index 00000000..de1dcddf
--- /dev/null
+++ b/apps/36-blocks/src/app/website/docs/docs.component.ts
@@ -0,0 +1,30 @@
+import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
+import { Meta, Title } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+import { WIDGET_EXAMPLE } from './widget-example';
+
+@Component({
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ selector: 'proxy-docs',
+ imports: [RouterModule],
+ templateUrl: './docs.component.html',
+})
+export class DocsComponent implements OnInit {
+ private readonly title = inject(Title);
+ private readonly meta = inject(Meta);
+ public readonly example = WIDGET_EXAMPLE;
+
+ public ngOnInit(): void {
+ this.title.setTitle('Integration guide — 36Blocks');
+ this.meta.updateTag({
+ name: 'description',
+ content:
+ 'Mount the 36Blocks authentication widget and understand application, session, and permission boundaries.',
+ });
+ this.meta.updateTag({ property: 'og:title', content: 'Integration guide — 36Blocks' });
+ this.meta.updateTag({
+ property: 'og:description',
+ content: 'Widget configuration and application security boundaries.',
+ });
+ }
+}
diff --git a/apps/36-blocks/src/app/website/docs/widget-example.ts b/apps/36-blocks/src/app/website/docs/widget-example.ts
new file mode 100644
index 00000000..13ca3908
--- /dev/null
+++ b/apps/36-blocks/src/app/website/docs/widget-example.ts
@@ -0,0 +1,22 @@
+/** Authorization mode mounts at the configured reference ID. */
+export const WIDGET_EXAMPLE = `
+
+
+`;
diff --git a/apps/36-blocks/src/app/website/home/home.component.html b/apps/36-blocks/src/app/website/home/home.component.html
index 8b059d69..af3d3a1e 100644
--- a/apps/36-blocks/src/app/website/home/home.component.html
+++ b/apps/36-blocks/src/app/website/home/home.component.html
@@ -38,8 +38,8 @@
- Launch secure user signup, login, roles, and access control in minutes — without building complex
- authentication systems from scratch.
+ Configure user signup, login, roles, and access controls for your application. Investigate failures and
+ manage users from one panel.
- Built for SaaS founders who want authentication done — without complexity.
+ Current prices, limits, and service commitments require confirmation for your environment.
- Everything you need to ship auth fast and scale confidently.
+ Understand your configuration and verify the integration before launch.
diff --git a/apps/36-blocks/src/app/website/pricing/pricing.component.ts b/apps/36-blocks/src/app/website/pricing/pricing.component.ts
index 1cb8bd34..9e089a14 100644
--- a/apps/36-blocks/src/app/website/pricing/pricing.component.ts
+++ b/apps/36-blocks/src/app/website/pricing/pricing.component.ts
@@ -1,8 +1,8 @@
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
-import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router';
+import { MatIconModule } from '@angular/material/icon';
export interface PricingFeature {
text: string;
@@ -37,7 +37,7 @@ export interface TrustItem {
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'proxy-pricing',
- imports: [CommonModule, RouterModule, MatIconModule],
+ imports: [CommonModule, MatIconModule, RouterModule],
templateUrl: './pricing.component.html',
})
export class PricingComponent implements OnInit {
@@ -48,112 +48,57 @@ export class PricingComponent implements OnInit {
this.titleService.setTitle('Pricing — 36Blocks');
this.metaService.updateTag({
name: 'description',
- content:
- 'Simple, transparent pricing for 36Blocks. Start free with 1,000 MAU. Upgrade as you grow. No hidden fees, no contracts.',
+ content: 'Confirm current 36Blocks pricing, usage limits, and service terms with the team.',
});
this.metaService.updateTag({ name: 'robots', content: 'index, follow' });
this.metaService.updateTag({ property: 'og:title', content: 'Pricing — 36Blocks' });
this.metaService.updateTag({
property: 'og:description',
- content: 'Start free. Scale with confidence. Transparent usage-based pricing.',
+ content: 'Contact the team for current pricing and service terms.',
});
this.metaService.updateTag({ property: 'og:type', content: 'website' });
}
public readonly plans: PricingPlan[] = [
{
- name: 'Starter',
- price: '$0',
- period: '/ month',
- description: 'Everything you need to get started with authentication.',
- highlight: false,
- features: [
- { text: '1,000 Monthly Active Users', included: true },
- { text: '3 Blocks', included: true },
- { text: '7-day logs', included: true },
- { text: '3-hour sessions', included: true },
- { text: 'Email authentication', included: true },
- { text: 'SMS (Not available)', included: false },
- { text: '36Blocks branding', included: true },
- ],
- cta: 'Start Free',
- ctaStyle: 'outline',
- },
- {
- name: 'Growth',
- price: '$49',
- period: '/ month',
- description: 'Scale your product with powerful auth and branding control.',
- highlight: true,
- badge: 'Most Popular',
- features: [
- { text: '10,000 MAU included', included: true },
- { text: '10 Blocks', included: true },
- { text: '30-day logs', included: true },
- { text: '12-hour sessions', included: true },
- { text: 'Custom branding', included: true },
- { text: 'SMS available (usage-based)', included: true },
- { text: 'Auto multi-org mapping', included: true },
- { text: 'Extra Usage: +$10 per 5k MAU', included: true, bold: true },
- { text: '+$5 per extra Block', included: true, bold: true },
- ],
- cta: 'Upgrade to Growth',
- ctaStyle: 'primary',
- },
- {
- name: 'Scale',
- price: '$149',
- period: '/ month',
- description: 'Enterprise-grade auth for high-volume, complex SaaS products.',
+ name: 'Plan and environment review',
+ price: 'Contact us',
+ period: '',
+ description: 'Confirm current commercial terms and enabled capabilities for your application.',
highlight: false,
features: [
- { text: '50,000 MAU included', included: true },
- { text: 'Unlimited Blocks', included: true },
- { text: '24-hour sessions', included: true },
- { text: 'Advanced org rules', included: true },
- { text: 'SMS available (usage-based)', included: true },
- { text: 'Extra Usage: +$25 per 25k MAU', included: true, bold: true },
+ { text: 'Review login methods and provider configuration', included: true },
+ { text: 'Confirm usage limits and billing terms', included: true },
+ { text: 'Confirm session and retention policies', included: true },
],
- cta: 'Choose Scale',
+ cta: 'Discuss your setup',
ctaStyle: 'outline',
},
];
public readonly infoBanners: InfoBanner[] = [
{
- icon: 'bar_chart',
- title: 'Transparent usage-based pricing',
+ icon: 'info',
+ title: 'Current terms require confirmation',
description:
- 'MAU = unique logged-in users per calendar month. Usage visible in real time. No hidden fees. No contracts.',
+ 'Plan names and limits in older material may differ from your environment. Confirm pricing, usage allowances, and enabled providers with the team before purchase.',
},
{
icon: 'sms',
- title: 'Optional Add-ons',
+ title: 'Provider dependencies',
description:
- 'Transactional SMS on paid plans. Billed per message by destination country. Requires a configurable monthly spending cap.',
+ 'SMS, email, and social login require the appropriate provider configuration. Confirm provider costs and availability for your environment.',
},
];
public readonly trustItems: TrustItem[] = [
+ { title: 'Widget integration', description: 'Use your configured block reference ID.', icon: 'code' },
+ { title: 'Application boundaries', description: 'Your product enforces business authorization.', icon: 'lock' },
+ { title: 'Environment review', description: 'Confirm enabled providers and service terms.', icon: 'settings' },
{
- title: '5-minute setup',
- description: 'Minimal configuration. Production-ready instantly.',
- icon: 'bolt',
- },
- {
- title: 'No vendor lock-in',
- description: 'Simple architecture and predictable APIs.',
- icon: 'lock_open',
- },
- {
- title: 'Cancel anytime',
- description: 'No contracts. Upgrade or downgrade freely.',
- icon: 'autorenew',
- },
- {
- title: 'Security-first',
- description: 'Encrypted in transit and at rest.',
- icon: 'security',
+ title: 'Investigation tools',
+ description: 'Inspect request failures and recorded activity.',
+ icon: 'search',
},
];
}
diff --git a/apps/36-blocks/src/app/website/privacy/privacy.component.html b/apps/36-blocks/src/app/website/privacy/privacy.component.html
index 7969cc56..68654959 100644
--- a/apps/36-blocks/src/app/website/privacy/privacy.component.html
+++ b/apps/36-blocks/src/app/website/privacy/privacy.component.html
@@ -179,9 +179,9 @@
4. Data Sharin
>
5. Data Retention
- Account data is retained for the life of your account. End-user authentication logs are retained per
- your plan (7 days on Starter, 30 days on Growth, 90 days on Scale). On account deletion, all data is
- purged within 30 days.
+ Retention and deletion schedules depend on the deployed environment and applicable service terms.
+ Contact the team to confirm the current policy for account data, request logs, and activity records
+ before sending personal data. No fixed retention or deletion schedule is published here.
- We take security seriously. Every layer of 36Blocks is designed with your users' data and trust in mind.
+ Understand the access boundaries and configuration that protect your connected applications.
-
Last updated: January 2026
-
-
-
-
-
-
-
-
-
-
-
Infrastructure & Hosting
-
- 36Blocks runs on enterprise-grade cloud infrastructure with automatic failover, redundancy, and
- geographic distribution. All services are containerised and isolated to prevent cross-tenant
- data leakage.
-
-
-
+
+
+
Authentication configuration
+
+ Blocks support configurable login methods, provider settings, session expiry, and hCaptcha integration.
+ Availability and enforcement depend on the selected block and deployment. Configure the providers and
+ verify failure, expiry, and recovery behavior for your application before launch.
+
-
-
-
-
-
-
-
-
Data Encryption
-
- All data is encrypted both in transit and at rest. We use TLS 1.2+ for all network communication
- and AES-256 for data stored on disk. Passwords are hashed using bcrypt and are never stored in
- plaintext.
-
-
- @for (
- item of [
- 'TLS 1.2+ for all API and dashboard traffic',
- 'AES-256 encryption at rest',
- 'bcrypt password hashing',
- 'Encrypted database backups',
- ];
- track item
- ) {
-
-
- {{ item }}
-
- }
-
-
-
+
+
Identity and application access
+
+ Authentication identifies a user. It does not grant access to every application or organization. Roles
+ and permissions help model membership, while each connected product remains responsible for resource
+ ownership and business authorization on its backend.
+
+
+ Do not rely on a browser callback or hidden UI control to protect a resource. Your application must
+ verify authentication results, expiry, membership, and permissions before granting access.
+
-
-
-
-
-
-
-
-
Authentication Security
-
- 36Blocks implements industry best practices for authentication flows including OTP expiry, rate
- limiting, brute-force protection, and bot prevention via hCaptcha.
-
-
- @for (
- item of [
- 'hCaptcha bot protection on all public endpoints',
- 'OTP codes expire within 10 minutes',
- 'Rate limiting on login and OTP requests',
- 'Session tokens with configurable expiry',
- 'Secure, HttpOnly cookies where applicable',
- ];
- track item
- ) {
-
-
- {{ item }}
-
- }
-
-
-
+
+
Investigation and activity history
+
+ Request logs help investigate API failures. Sensitive-operation activity history records the operations
+ that have been instrumented, including Login as attempts. Coverage and availability depend on the
+ deployed version and permissions.
+
+
+ Confirm retention, operator access, and the required investigation coverage with the team. Use activity
+ records within their documented coverage.
+
-
-
-
-
-
-
-
-
- Access Control & Audit Logs
-
-
- Role-Based Access Control (RBAC) is built into the core of 36Blocks. Every action is logged with
- a full audit trail so you can see who did what, when. Logs are retained per your plan and are
- tamper-evident.
-
-
-
+
+
Deployment responsibilities
+
+ Transport protection, database encryption, backups, recovery, and availability depend on the hosting
+ environment. Confirm these controls and any service commitments for your deployment. Ask the team for
+ environment-specific availability and recovery commitments.
+
-
-
-
-
-
-
-
-
Responsible Disclosure
-
- If you discover a security vulnerability, please contact us at
- security@36blocks.com. We will respond within 48 hours and work with you to resolve the issue responsibly.
-
-
-
+
+
Report a security concern
+
+ Contact the team through the contact page.
+ Include the affected flow and safe reproduction steps. Do not include passwords, session tokens, or
+ personal data in your initial message.
+
diff --git a/apps/36-blocks/src/app/website/security/security.component.ts b/apps/36-blocks/src/app/website/security/security.component.ts
index 61aa948e..92383c7e 100644
--- a/apps/36-blocks/src/app/website/security/security.component.ts
+++ b/apps/36-blocks/src/app/website/security/security.component.ts
@@ -17,13 +17,13 @@ export class SecurityComponent implements OnInit {
this.metaService.updateTag({
name: 'description',
content:
- 'Learn how 36Blocks keeps your users safe with enterprise-grade encryption, hCaptcha bot protection, rate limiting, RBAC audit logs, and responsible disclosure.',
+ 'Understand authentication configuration, application access boundaries, and investigation coverage in 36Blocks.',
});
this.metaService.updateTag({ name: 'robots', content: 'index, follow' });
this.metaService.updateTag({ property: 'og:title', content: 'Security — 36Blocks' });
this.metaService.updateTag({
property: 'og:description',
- content: 'Enterprise-grade security built into every layer of 36Blocks.',
+ content: 'Authentication configuration and application security boundaries.',
});
this.metaService.updateTag({ property: 'og:type', content: 'website' });
}
diff --git a/apps/36-blocks/src/app/website/terms/terms.component.html b/apps/36-blocks/src/app/website/terms/terms.component.html
index c6174084..e8663699 100644
--- a/apps/36-blocks/src/app/website/terms/terms.component.html
+++ b/apps/36-blocks/src/app/website/terms/terms.component.html
@@ -18,9 +18,9 @@
1. Acceptance of Terms
- By accessing or using the 36Blocks platform, APIs, SDKs, or any related services (collectively, the
- "Service"), you agree to be bound by these Terms of Service ("Terms"). If you do not agree, do not use
- the Service.
+ By accessing or using the 36Blocks platform, APIs, browser widget, or any related services
+ (collectively, the "Service"), you agree to be bound by these Terms of Service ("Terms"). If you do not
+ agree, do not use the Service.
` acts as the mount point for the widget.
+2. The `proxy-auth.js` script registers a global `window.initVerification(config)` function.
+3. Calling `initVerification` creates and appends a `` custom element inside the container div.
+4. The widget fetches its configuration from the API using `referenceId`, renders buttons, and handles the full auth flow.
+
+---
+
+## Base Script Loader
+
+The widget uses a **2-script pattern**. The first script defines the `configuration` object; the second loads `proxy-auth.js` and calls `initVerification(configuration)` via the `onload` attribute.
+
+Replace `SCRIPT_URL` with the actual hosted URL of `proxy-auth.js` (e.g. `https://yourdomain.com/assets/proxy-auth/proxy-auth.js`).
+
+---
+
+## Example 1 — Authorization (Login)
+
+Default widget type. Shows OTP, social login, and/or password login buttons based on your dashboard configuration.
+
+```html
+
+
+
+
+
+```
+
+---
+
+## Example 2 — User Profile
+
+Displays the authenticated user's profile. Requires an `authToken` obtained from a previous login success callback.
+
+```html
+
+
+
+
+```
+
+---
+
+## Example 3 — User Management
+
+Shows a role & user management panel. Requires an `authToken`. Use `isRolePermission`, `include_role_ids`, and `exclude_role_ids` to control visibility.
+
+```html
+
+
+
+
+```
+
+---
+
+## Example 4 — Organization Details
+
+Renders the organization details view. Use `showCompanyDetails` to include the company info section.
+
+```html
+
+
+
+
+```
+
+---
+
+## Example 5 — Subscription
+
+Shows subscription plans. Set `isLogin: true` to display a login flow alongside the plans.
+
+```html
+
+
+
+
+```
+
+---
+
+## Example 6 — Authorization (Advanced)
+
+Uses `version: 'v2'` UI, icon-only social buttons, custom input field position, and registration-only mode. URL query parameters can also pre-fill registration fields.
+
+```html
+
+
+
+
+```
+
+---
+
+## All Config Properties
+
+| Property | Type | Default | Description |
+|---|---|---|---|
+| `referenceId` | `string` | — | **Required.** Widget ID from the dashboard. Must also be the `id` of the container `
`. |
+| `authToken` | `string` | — | Encrypted auth token for authenticated-user views (`user-profile`, `user-management`, `organization-details`). |
+| `type` | `string` | `'authorization'` | Widget mode. One of: `'authorization'` · `'user-profile'` · `'user-management'` · `'organization-details'` · `'subscription'` |
+| `theme` | `string` | `'system'` | Color theme. One of: `'system'` · `'light'` · `'dark'` |
+| `target` | `string` | `'_self'` | Link open target. `'_self'` or `'_blank'`. |
+| `version` | `string` | `'v1'` | Widget UI version. `'v1'` or `'v2'`. |
+| `showCompanyDetails` | `boolean` | `false` | Show company details section in `organization-details` view. |
+| `isHidden` | `boolean` | `false` | Mount widget hidden initially (useful for `user-management` dialog trigger). |
+| `isRolePermission` | `boolean` | `false` | Enable role-based permission UI in `user-management`. |
+| `isPreview` | `boolean` | `false` | Preview mode — no live data mutations. Useful for testing UI. |
+| `isLogin` | `boolean` | `false` | Show login UI alongside subscription plans (`subscription` type). |
+| `isRegisterFormOnly` | `boolean` | `false` | Show only the registration form, skipping the login/auth buttons. |
+| `loginRedirectUrl` | `string` | — | URL to redirect to after a successful login. |
+| `redirect_path` | `string` | — | Path to redirect after login (e.g. `'/login'`). Internally forwarded as `addInfo.redirect_path` in the API payload. Primarily used to obtain a `proxy_auth_token` in the admin panel during preview. |
+| `input_fields` | `string` | `'top'` | Input field position. `'top'` or `'bottom'`. |
+| `show_social_login_icons` | `boolean` | `false` | Show social login as icon-only buttons instead of full-width. |
+| `exclude_role_ids` | `number[]` | — | Role IDs to hide in the `user-management` view. |
+| `include_role_ids` | `number[]` | — | Role IDs to show in the `user-management` view. |
+| `success` | `function(data)` | — | **Required.** Callback invoked on successful action. `data` contains the verified auth token or result. |
+| `failure` | `function(error)` | — | Callback invoked on failure. `error` contains the failure reason. |
+
+---
+
+## Notes & Rules
+
+- The container `