This document defines the security posture of the Goodwill KPI Framework. It establishes the threat model, access controls, data handling constraints, and operational guidelines that govern how the system is deployed and used in environments where auditability, data integrity, and organizational trust are non-negotiable.
The Goodwill KPI Framework is a read-only, stateless calculation service. Its security model is deliberately minimal: there is no persistent state, no authentication-gated write path, and no user data retained between requests. This document exists to make that posture explicit, auditable, and defensible.
In scope:
- The FastAPI application (
app/main.py) and its four HTTP routes - The
goodwill.metricspure functions and their input validation layer - The
goodwill.configweight-loading mechanism, including environment variable handling - Deployment on Render.com as described in
render.yaml - The read-only browser dashboard (
app/templates/dashboard.html)
Out of scope:
- Network infrastructure, TLS termination, and reverse-proxy configuration (responsibility of the hosting platform)
- Identity and access management for the host environment
- Third-party integrations or downstream consumers of the exported data
- Modifications to
goodwill/metrics.py(the equations are immutable)
The framework exposes three deterministic goodwill equations—General Goodwill (G), Consumer Goodwill (CG), and Unified Goodwill Score (UGS)—through a stateless HTTP API. All computation occurs server-side in pure Python functions. No state is written, cached, or persisted. Each request is fully self-contained and independent of every other request.
The dashboard is a read-only interface. It does not authenticate users, store
sessions, or issue tokens. It submits form data to the /api/goodwill/calculate
endpoint and renders the response. The export endpoint (/api/goodwill/export)
returns a file stream built entirely from the request payload and the equation
outputs; no files are written to disk.
This architecture eliminates entire classes of risk—injection of persistent state, unauthorized data retrieval from a data store, and privilege escalation— by design.
The framework follows a minimal-exposure threat model. The attack surface is bounded by:
| Surface | Exposure | Mitigation |
|---|---|---|
| HTTP API inputs | Numeric values, bounded [0, 100] | Pydantic Field validation + metrics.py ValueError guards |
| Weight overrides | Optional floats in request body | Passed directly to pure functions; no eval, no exec |
| Export endpoint | Request-driven file stream | Built entirely from validated inputs; no disk I/O |
| Environment variables | Weight defaults at startup | Validated by config._float_env; non-finite values rejected |
| Dashboard HTML | Static Jinja2 template | No user-supplied content rendered into the template |
All metric inputs are validated at two independent layers:
- Pydantic model (
GoodwillInput): enforces type correctness and range constraints (ge=0, le=100,gt=0for T) before any application logic runs. goodwill.metricsvalidation (_validate_metric,_validate_T,_validate_real_number): enforces the same constraints again inside the pure functions. This ensures that the math layer is safe even if called directly without going through the API.
Both layers raise explicit, descriptive errors. No silent truncation, clamping, or default substitution occurs for out-of-range inputs.
The framework stores no data. It holds no database connections, no file handles, no session stores, and no in-memory caches between requests. Every request is stateless and isolated. Logs, if any, are the responsibility of the deployment platform and are not generated by the application.
Runtime dependencies are limited to FastAPI, Uvicorn, Pydantic, Jinja2, openpyxl, and their transitive dependencies. Development dependencies add pytest, httpx, and related tooling. No cryptographic libraries, authentication frameworks, or external service clients are included.
Because no state is persisted, every result can be reproduced exactly by replaying the original request payload. Operators who require an audit trail must capture request/response pairs at the infrastructure layer (e.g., load balancer or reverse-proxy logs), as the application itself does not log calculations.
The export endpoint supports downstream auditability: the generated file contains every input, every weight, every term breakdown, and every final score. An auditor who receives an export file can verify each score by hand using the documented equations.
All three equations are deterministic, pure functions with no external dependencies, random state, or time-based variation. Given identical inputs and identical weight configuration, results are byte-for-byte identical across runs, deployments, and Python versions within the supported range (3.11, 3.12).
The framework does not collect, store, or transmit personally identifiable information (PII). It processes only aggregated organizational metrics. Operators are responsible for ensuring that the numeric inputs they supply do not embed PII or confidential data in a form that could be reconstructed from logs.
Weight configuration is controlled via environment variables. Any deviation from default weights must be documented and traceable to an organizational decision, as weight choices directly affect score interpretation.
-
Run behind a reverse proxy. The application does not terminate TLS. All production deployments must be fronted by a TLS-terminating proxy (Render.com handles this automatically).
-
Restrict inbound access if needed. The API has no authentication layer. If the deployment context requires access control, implement it at the infrastructure layer (e.g., IP allowlisting, OAuth2 proxy, or API gateway).
-
Pin dependency versions. Use
requirements.txtas-is. Do not upgrade dependencies without reviewing changelogs and re-running the test suite. -
Validate weight environment variables before deployment. Set and verify all
GOODWILL_*environment variables in the deployment environment before the service starts. Invalid values will cause aValueErrorat import time and prevent the service from starting. -
Do not expose the service on a public port without TLS in production. The Render.com deployment handles TLS termination. Self-hosted deployments must configure TLS at the proxy layer.
If a security vulnerability is identified in this repository, open a private security advisory via GitHub's Security → Advisories tab. Do not disclose vulnerability details in public issues or pull requests until a fix has been prepared and reviewed.
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Malformed numeric inputs causing unexpected computation behavior | Low | Low | Dual-layer validation (Pydantic + metrics.py) raises ValueError before any math executes |
| Unauthenticated access to calculation results | Medium | Low | All results are derived from caller-supplied inputs; no confidential server-side data is exposed |
| Weight misconfiguration producing misleading scores | Medium | Medium | Config validation at startup; operators must document and audit any non-default weight configuration |
| Dependency vulnerability in FastAPI/Pydantic/openpyxl | Low–Medium | Medium | Monitor upstream security advisories; pin versions; re-run tests after upgrades |
| Export file containing sensitive inputs submitted by users | Low | Medium | Operators must ensure that inputs do not contain PII; the framework itself does not filter or redact content |
| Denial of service via high-volume requests | Low | Low–Medium | No computation-intensive loops; equations are O(1); rate limiting should be applied at the infrastructure layer if needed |
| Environment variable injection in CI/CD pipeline | Low | High | Validate all GOODWILL_* environment variables in deployment pipelines; use secrets management for production |
The Goodwill KPI Framework is a stateless, read-only calculation service with a deliberately minimal security surface. It stores no state, performs no authentication, and exposes no confidential data. Its primary security properties are input validation rigor, deterministic reproducibility, and architectural simplicity.
Operators are responsible for TLS termination, access control at the infrastructure layer, dependency management, and ensuring that the numeric inputs they supply do not embed sensitive information. Within those boundaries, the framework is designed to be deployed safely in governance-aware environments without additional application-layer security controls.