Skip to content
decionisPublic

About

The open-source customer support decisioning platform: see what is happening across your accounts, triage one queue of limit reviews, expansion, friction and KYC escalations with the evidence behind each, and act before the customer asks. Decisions are recorded and executed by Decionis.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

195 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Decionis

Decionis Steward

Verify Audit OpenSSF Scorecard License: Apache 2.0

The open-source customer support decisioning platform: see what is happening, triage one queue, act before the customer asks.

Try the live demo → — the whole operator workflow on deterministic fixtures. No account, no credentials, nothing to install.

Steward is the open-source (Apache-2.0) customer support decisioning platform. Customer operations, risk and revenue teams see what is happening across their accounts as it happens: the usage, support, settlement, CRM and KYC signals, each with its source, its record, when it was observed and whether the source is healthy, weighed at the moment a decision is made rather than read from a stale profile. They triage one queue of processing-limit reviews, expansion outreach, friction interventions and KYC/KYB escalations, and act before the customer asks. An approver accepts, holds or rejects; the review is recorded and forwarded to Decionis, the authority behind Steward, which executes the change and files the Decision Dossier. Nothing changes until it does.

The problem it solves: in regulated fintech, the people closest to the customer can see that an account is ready for a higher processing limit or is about to churn, but they cannot act on it without an auditable, policy-bound path. Steward is that path. Every action an operator takes here becomes a reviewed, attributable event upstream — never an ad-hoc change made in a spreadsheet.

The Steward control center: portfolio health, evidence coverage, and the governed action queue

What Steward is not

This boundary is the most important thing to understand before contributing.

Steward does not own policy evaluation, identity resolution, execution grants, or Decision Dossiers. Those remain authoritative in the Decionis platform. Steward collects the signals that become context, presents what the platform decided, and forwards what the operator does:

Steward owns Decionis platform owns
The operator UI and review workflow Policy evaluation and the customer_ops policy pack
Server-side orchestration (the BFF) Identity resolution and the weighing of signals
Signal connectors and the credentials they run with, server-side Execution grants, Decision Dossiers, the audit ledger
Typed, runtime-validated API contracts The authoritative record of every review
Formatting and presentation policy

An operator can accept a review in Steward. That acceptance cannot, by itself, change a processing limit or a policy. Steward forwards the review; Decionis decides, executes, and returns the resulting state and a dossier reference.

Steward also collects. Connectors under infra/connectors/ reach the operator's own systems (CRM, support desk, ERP, MCP servers, uploaded documents, file servers), produce signals in one small shape, and forward them to the platform over the Decionis Protocol, where they are resolved to accounts and weighed. Steward stores none of it. docs/SignalConnectors.md records the decision and the workstreams; the first, the connector framework and the Signal sources page, is in the tree.

Steward also keeps its own records. Its users and sessions, the Decionis workspace connection, the signal sources it is configured with, the decisions it showed, the reviews its operators recorded and their activities live in a database the operator chooses: the embedded one under STEWARD_DATA_DIR with nothing configured, or PostgreSQL, MySQL, SQL Server or Oracle Database named by STEWARD_DATABASE_URL. A signal's content is never written to it. The schema changes only through portable migrations, applied at start. docs/Persistence.md records the decision.

OpenCore.md states the same boundary as a business model: what is free forever, what Decionis operates, and the one point where commerce enters the flow. docs/Distribution.md is the plan for shipping Steward as a container and being found by the agents that evaluate it.

Browser
  -> Steward Next.js server / BFF          <- this repository
    -> signal connectors: CRM, ERP, MCP servers, documents, file servers
    -> Decionis /v1/cdi APIs
      -> signal ingestion, identity resolution, weighting
      -> customer_ops policy pack
      -> execution grants, dossiers, ledger

Install

One application, four ways to run it. The image, the tarball and the source are built from the same commit, and nothing about the trust boundary differs between them.

Where How Page
Docker docker run -p 3000:3000 ghcr.io/decionis/steward:<version>; the same digest as docker.io/decionis/steward; two architectures, non-root, attested docs/Docker.md
Release tarball decionis-steward-<version>.tar.gz from the releases, with its SBOM and provenance Deployment
From source pnpm install && pnpm dev, demo mode, no credentials Quickstart
Hosted demo https://decionis-steward.vercel.app, demo mode, nothing to install What you get

Every path starts in demo mode and ends at the same place: connecting a Decionis workspace, which OpenCore.md explains costs nothing until a review is meant to execute. An agent evaluating a deployment reads /llms.txt on any of them.

Quickstart

Requires Node >= 20 and pnpm 9. No Decionis credentials are needed — the app boots against deterministic demo fixtures.

cp .env.example .env.local
pnpm install
pnpm dev

Open http://localhost:3000.

What you get in demo mode

STEWARD_DATA_MODE=demo (the default outside production) serves every screen from infra/demo/DemoStewardData.ts via DemoStewardRepository. You are signed in as a fixture operator with ADMIN and APPROVER roles in the demo-fintech organization, so the full review flow is exercisable end to end:

  • Portfolio dashboard (/) — account health, evidence coverage, the summary counters that drive triage, the decisions that reached an outcome under "Recently resolved", and "Plan support": the portfolio by region and by segment, friction first, so effort goes where it is needed.
  • Opportunity queue — friction interventions, KYC/KYB escalations, processing-limit reviews, and expansion outreach, each with its rationale, confidence, linked evidence, and the platform's "Why this disposition". Deliberate inaction is grouped beneath the queue rather than hidden.
  • Account detail (/accounts/[id]) — evidence signals with their context class and a coverage strip, connection health, applicable policy, the state of the context at the moment of review, and the decision timeline.
  • Signal sources (/signals) — the systems this deployment collects from, each with its kind, the categories it yields, health, when it was last collected and how many signals; "Collect now" pulls a batch and forwards it, and reports how many were accepted upstream.

The governed action queue: each recommendation carries its rationale, confidence, evidence coverage, and dossier reference

Every review control is captioned "Records a review only; no downstream limit is changed." That is the trust boundary stated in the interface, not only in the documentation. It is also the free tier: the top bar's "Connect your platform" link leads to the Decionis sign-in handoff, and OpenCore.md says what is paid from there.

Account detail: processing envelope, the recommendation under review, correlated evidence, and connector health

Reviews submitted in demo mode return a deterministic result. Nothing is persisted and no downstream action is executed.

Signal sources: the systems this deployment collects from, their health, when each was last collected, and "Collect now"

In demo mode the sources are fixtures and collecting them forwards nowhere. In live mode a batch goes to the Decionis Protocol's published signal ingress, authenticated by the connector's webhook secret from your deployment bundle, and collection answers 503 until those two values are set, rather than pretending a batch was accepted.

Screenshots are generated from a running instance with pnpm screenshots, so they can be refreshed rather than left to drift out of date.

Configuration

All configuration is parsed and validated once, at startup, by StewardRuntimeConfig.fromEnvironment(). Invalid or missing required values fail fast rather than degrading at request time.

Variable Required Default Purpose
STEWARD_DATA_MODE no live in production, else demo Selects DemoStewardRepository or DecionisStewardRepository.
DECIONIS_API_BASE_URL in live mode — Decionis API origin. Startup throws in live mode if unset.
DECIONIS_STEWARD_SERVICE_TOKEN no — Server-to-server fallback credential. Prefer a user session.
STEWARD_ACCESS_TOKEN_COOKIE no decionis_access_token Cookie carrying the Decionis access token.
STEWARD_ORG_ID_COOKIE no decionis_org_id Cookie carrying the organization scope.
NEXT_PUBLIC_DECIONIS_SIGN_IN_URL no https://decionis.com/sign-in External identity handoff target used by /sign-in.
DECIONIS_CONNECTOR_ID to forward — The signal connector issued by the Decionis deployment bundle.
DECIONIS_WEBHOOK_SECRET to forward — Its webhook secret; sent in a header, never in a URL or a log.
DECIONIS_WEBHOOK_URL no <API origin>/v1/signals/webhooks/<id> The ingress URL from the bundle, if it differs.
STEWARD_DATABASE_URL no the embedded database postgres://, mysql://, mssql://, oracle:// or sqlite: selects where Steward's records live.
STEWARD_DATA_DIR no ./data Where the embedded database file lives when no URL is set; a volume in the container.
STEWARD_DATABASE_MIGRATE no on-start off refuses to serve a schema that is behind instead of migrating it.

Two further cookies are read opportunistically in live mode and are not required: decionis_display_name (URL-encoded, for the app shell) and decionis_roles (a comma-separated subset of VIEWER,OPERATOR,APPROVER,ADMIN; anything unrecognized is dropped, and an empty result falls back to VIEWER).

The connector id and the webhook secret are issued together by a signal-mapping session in your Decionis workspace (decionis.com/docs/signal-mapping); setting one without the other fails at startup. Without them, the Signal sources page still lists and checks sources, and "Collect now" answers 503.

No API credential is ever exposed to browser code. The upstream client is server-only, and its request timeout is currently fixed at 8s in StewardRuntimeConfig.

Live mode

STEWARD_DATA_MODE=live
DECIONIS_API_BASE_URL=https://api.decionis.com

In live mode middleware.ts requires a Decionis session on every path except /api/health, /sign-in, and the discovery files /llms.txt and /llms-full.txt. Page requests without one are redirected to /sign-in?returnTo=…; API requests receive 401 {"error":"UNAUTHORIZED"}. BFF callers may instead present an Authorization: Bearer token with an X-Decionis-Org-Id header.

A live API failure surfaces as an error. It never falls back to demo fixtures — silently showing fabricated data to an operator making a regulated decision is treated as a defect, not a resilience feature.

Routes this app exposes

Route Method Notes
/api/steward/portfolio GET Portfolio snapshot for the session's org.
/api/steward/accounts/[id] GET 404 when the account is unknown.
/api/steward/opportunities GET Opportunity queue.
/api/steward/opportunities/[id]/review POST Requires APPROVER or ADMIN, else 403.
/api/steward/signals/sources GET Configured signal sources, with health.
/api/steward/signals/sources/[id]/collect POST Requires OPERATOR or above, else 403; 503 until forwarding is configured.
/api/health GET Unauthenticated liveness probe; reports the database opened at start.
/llms.txt, /llms-full.txt GET Machine discovery; no session, indexable.

Upstream endpoints it expects

  • GET /v1/cdi/portfolio
  • GET /v1/cdi/accounts/:accountId
  • GET /v1/cdi/opportunities
  • POST /v1/cdi/opportunities/:opportunityId/reviews
  • POST /v1/signals/webhooks/:connectorId — the Protocol's published signal ingress (decionis.com/docs/webhooks), authenticated by the connector's webhook secret in the x-webhook-secret header; where a collected batch goes

Every upstream response is parsed through a Zod contract in domain/ before it is allowed into the application layer, so schema drift upstream fails loudly at the boundary instead of rendering as a subtly wrong number on a dashboard.

Deployment

The published image is the quickest path. It is built for linux/amd64 and linux/arm64, runs unprivileged, defaults to demo mode, and carries a signed provenance attestation:

docker run -p 3000:3000 ghcr.io/decionis/steward:<version>   # demo mode, no credentials
gh attestation verify oci://ghcr.io/decionis/steward:<version> --repo decionis/steward

The same digest is on Docker Hub as decionis/steward. docs/Docker.md covers tags, live mode, verification and what the image does and does not hold.

next.config.ts sets output: "standalone", so the build emits a self-contained server. The included Dockerfile packages it:

docker build -t decionis-steward .
docker run -p 3000:3000 decionis-steward          # demo mode, no credentials
docker run -p 3000:3000 \
  -e STEWARD_DATA_MODE=live \
  -e DECIONIS_API_BASE_URL=https://api.decionis.com \
  decionis-steward                                 # live mode

The image runs as a non-root user, disables Next telemetry, and declares a HEALTHCHECK against /api/health — which is exempt from the session middleware precisely so probes work without a Decionis session.

If you deploy without Docker, note that .next/standalone is not self-sufficient: next build emits static assets separately, and .next/static must be copied alongside the server. The Dockerfile and release workflow both do this.

Tagged releases ship a deployable tarball, a CycloneDX SBOM, and a signed SLSA provenance attestation. Verify an artifact came from this repository before deploying it:

gh attestation verify decionis-steward-<version>.tar.gz --repo decionis/steward

Architecture

Four layers, one direction of dependency: app → application → domain, with infra supplying implementations through a composition root and presentation holding formatting policy only.

Layer Responsibility
app/ Next.js routes, the BFF, and framework entrypoints.
application/ Use-case services and permission checks (OpportunityService, …).
domain/ Typed, runtime-validated Steward contracts. No I/O.
infra/ Gateways, repositories, config, errors, demo data, composition.
presentation/ Formatting and presentation policy.

Swapping demo for live is a single decision in StewardRepositoryFactory behind the StewardRepository interface — the application and UI layers cannot tell the difference. See Architecture.md for the full boundary and directory map.

docs/ContextEngineering.md maps the design onto the context-engineering framework (sense, interpret, arbitrate, act) and records the plan for making each of the platform's decisions more legible to the operator reviewing it.

Development

pnpm dev              # Next.js dev server
pnpm test             # Vitest
pnpm test:watch       # Vitest in watch mode
pnpm lint             # ESLint
pnpm typecheck        # tsc --noEmit
pnpm format:fix       # Prettier write
pnpm verify           # format + lint + typecheck + test + build
pnpm licenses:check   # Fail on a dependency outside the approved license policy
pnpm licenses:list    # Production dependency licenses
pnpm screenshots      # Regenerate the README screenshots from a running instance

pnpm verify is the gate — run it before opening a pull request.

CI runs it on Node 20 and 22 (.github/workflows/verify.yml), and separately runs the supply-chain gate (.github/workflows/audit.yml) — a production-tree audit at any severity, a whole-tree audit at high and critical, and the license policy check — on every pull request and again weekly, so an advisory published against an unchanged tree still surfaces.

Dependency policy

Production dependencies must carry a license in the approved set enforced by CheckLicensePolicy.mjs. Adding a dependency under any other license requires an explicit, package-scoped exception in that file and a recorded rationale in ThirdPartyLicenses.md.

package.json carries a pnpm.overrides block pinning postcss, nanoid, and sharp above known vulnerable ranges. These are forward pins to patched releases, not version freezes — remove each once the upstream next range resolves past it on its own.

Conventions

  • Feature and domain files use PascalCase (AccountService.ts, CustomerOpportunity.ts). Framework-required files keep Next.js naming (page.tsx, layout.tsx, route.ts, middleware.ts), as do directory segments.
  • Classes and interfaces over loose utility functions. Each module gets one reason to change.
  • camelCase for variables, properties, and methods.
  • Full rules in coding.rule.md.

Non-negotiable rules

  1. Evidence may adapt; policy authority stays deterministic.
  2. Demo mode is explicit. A live API failure never falls back to fixtures.
  3. Review actions are role-gated in application/ and forwarded to Decionis — the UI is not the enforcement point.
  4. No credential, token, or connector secret reaches client-side code.
  5. A connector reaches only the host its configured source names, and nothing it collects is stored here. Collection is gated in application/ like reviews; the platform resolves and weighs what it receives.
  6. Steward's database holds Steward's own records and never a signal's content, and its schema changes only through a portable migration under infra/persistence/migrations/, reviewed like code and applied at start.

Security

This repository holds no secrets and no policy logic, which is what makes it safe to develop against in the open.

The trust boundary is enforced in six files, and each is covered by tests you can run:

Enforcement Code Tests
Session gating, 401-vs-redirect, health-probe exemption middleware.ts middleware.test.ts
Role parsing and the VIEWER privilege floor StewardSessionResolver.ts StewardSessionResolver.test.ts
Credential handling and boundary schema validation JsonHttpClient.ts JsonHttpClient.test.ts
Role-gated review forwarding OpportunityService.ts OpportunityService.test.ts
Role-gated signal collection and forwarding SignalService.ts SignalService.test.ts
Migrations at start, failing closed when behind Migrator.ts Persistence.test.ts

Four properties the tests assert directly: the access token never appears in a request URL, only in the Authorization header; no client component ever receives the session, so the token is never serialized into a page payload; an unrecognized or wrong-case role claim resolves to VIEWER rather than to an empty role set; and an unhandled error maps to a generic 500 that leaks no internal detail.

Evaluating Steward as a vendor? EvidencePack.md maps the usual security-review questions to the artifact that answers each one, and states the gaps as plainly as the strengths.

ThreatModel.md sets out the assets, trust boundaries, eight named threats with the code and test backing each mitigation, the security headers this app sets — and, deliberately, the gaps we have accepted rather than fixed.

Reporting a vulnerability: do not open a public issue. See SECURITY.md for the private disclosure process, scope, and response targets.

Contributing

Contributions are welcome. Start with CONTRIBUTING.md for setup, the review gate, conventions, and DCO sign-off.

Run pnpm verify before opening a pull request — a green local run means a green CI run.

Read the trust boundary above before proposing anything that moves decision authority into this repository. Evaluating policy locally, persisting customer data in this tier, or falling back to fixtures when a live call fails are the changes this project will not accept, and CONTRIBUTING.md says so up front so you find out from a document rather than from a closed pull request.

Participation is governed by our Code of Conduct. Contributions are accepted under Apache-2.0 per section 5 of the LICENSE; there is no CLA.

License

Licensed under the Apache License, Version 2.0 — see LICENSE and NOTICE. Copyright 2026 Decionis, Inc.

Apache-2.0 is the default license across Decionis projects: permissive, with an express patent grant.

Third-party components and their licenses are inventoried in ThirdPartyLicenses.md. No dependency imposes a reciprocal obligation on this codebase.

package.json is marked "private": true. That prevents accidental publication to npm — this is a deployable application, not a library — and does not restrict use of the source under Apache-2.0.

Project status

Formerly "Decionis CDI". Renamed to Steward in August 2026, before external adoption. The GitHub URL redirects, but releases v0.1.0–v0.1.2 keep decionis-cdi-* artifact names — those names are bound into signed provenance attestations and are left as the historical record.

Public and Apache-2.0 licensed. Latest release v0.3.0; pre-1.0 and under active development, so contracts in domain/ may change without a deprecation period before 1.0.0 — pin exactly if you integrate against those types. CHANGELOG.md records what has shipped.

Evaluating Steward as a vendor? EvidencePack.md maps the usual security-review questions to the artifact that answers each, and states the gaps as plainly as the strengths. OpenSource.md is the record of how this repository was prepared for public release, including what was found wrong along the way.

About

The open-source customer support decisioning platform: see what is happening across your accounts, triage one queue of limit reviews, expansion, friction and KYC escalations with the evidence behind each, and act before the customer asks. Decisions are recorded and executed by Decionis.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages