generated from coulomb/repo-seed
Add small SaaS profile proof
This commit is contained in:
12
README.md
12
README.md
@@ -23,6 +23,9 @@ PYTHONPATH=src python3 -m info_tech_canon validate
|
|||||||
PYTHONPATH=src python3 -m info_tech_canon graph
|
PYTHONPATH=src python3 -m info_tech_canon graph
|
||||||
PYTHONPATH=src python3 -m info_tech_canon index
|
PYTHONPATH=src python3 -m info_tech_canon index
|
||||||
PYTHONPATH=src python3 -m info_tech_canon views
|
PYTHONPATH=src python3 -m info_tech_canon views
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile inspect small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile validate small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile graph small-saas
|
||||||
PYTHONPATH=src python3 -m info_tech_canon api --host 127.0.0.1 --port 8765
|
PYTHONPATH=src python3 -m info_tech_canon api --host 127.0.0.1 --port 8765
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -43,6 +46,8 @@ After package installation, the same commands are available through the
|
|||||||
- `GET /views`
|
- `GET /views`
|
||||||
- `GET /views/{name}`
|
- `GET /views/{name}`
|
||||||
- `GET /profiles/{profile}/inspect`
|
- `GET /profiles/{profile}/inspect`
|
||||||
|
- `GET /profiles/{profile}/validate`
|
||||||
|
- `GET /profiles/{profile}/graph`
|
||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|
||||||
@@ -52,3 +57,10 @@ make index
|
|||||||
make tree
|
make tree
|
||||||
make agent-briefs
|
make agent-briefs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## First Profile Proof
|
||||||
|
|
||||||
|
The first executable profile proof is `small-saas`. It lives under
|
||||||
|
`infospace/profiles/small-saas/` and includes connected example artifacts for a
|
||||||
|
tenant-aware SaaS service: service, system, tenants, user, team, dataset,
|
||||||
|
deployment, task, policy, control, evidence, and incident.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
This brief summarizes the current canon service surface for agents.
|
This brief summarizes the current canon service surface for agents.
|
||||||
|
|
||||||
- Infospace slug: `canon`
|
- Infospace slug: `canon`
|
||||||
- Artifact count: 15
|
- Artifact count: 29
|
||||||
- Primary confidence command: `make validate`
|
- Primary confidence command: `make validate`
|
||||||
- Refresh generated indexes and views with: `make index`
|
- Refresh generated indexes and views with: `make index`
|
||||||
|
|
||||||
|
|||||||
@@ -224,3 +224,252 @@ artifacts:
|
|||||||
target: model/task
|
target: model/task
|
||||||
- type: imports
|
- type: imports
|
||||||
target: standard/tagging
|
target: standard/tagging
|
||||||
|
- id: profile/small-saas
|
||||||
|
path: profiles/small-saas/profile.yaml
|
||||||
|
kind: profile
|
||||||
|
title: Small SaaS System Profile
|
||||||
|
provenance:
|
||||||
|
source_path: infospace/profiles/small-saas/profile.yaml
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: conforms_to
|
||||||
|
target: kernel/itc-core
|
||||||
|
- type: requires
|
||||||
|
target: model/landscape
|
||||||
|
- type: requires
|
||||||
|
target: model/organization
|
||||||
|
- type: requires
|
||||||
|
target: model/governance
|
||||||
|
- type: requires
|
||||||
|
target: model/access-control
|
||||||
|
- type: requires
|
||||||
|
target: model/security
|
||||||
|
- type: requires
|
||||||
|
target: model/data
|
||||||
|
- type: requires
|
||||||
|
target: model/devsecops
|
||||||
|
- type: requires
|
||||||
|
target: model/network
|
||||||
|
- type: requires
|
||||||
|
target: model/observability
|
||||||
|
- type: requires
|
||||||
|
target: model/task
|
||||||
|
- type: requires
|
||||||
|
target: standard/tagging
|
||||||
|
- type: requires
|
||||||
|
target: standard/caring
|
||||||
|
- id: small-saas/service/billing-portal
|
||||||
|
path: profiles/small-saas/artifacts/service.billing-portal.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Billing Portal Service
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/landscape
|
||||||
|
- type: part_of
|
||||||
|
target: small-saas/system/billing-system
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- id: small-saas/system/billing-system
|
||||||
|
path: profiles/small-saas/artifacts/system.billing-system.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Small SaaS Billing System
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/landscape
|
||||||
|
- type: serves
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: serves
|
||||||
|
target: small-saas/tenant/globex
|
||||||
|
- id: small-saas/tenant/acme
|
||||||
|
path: profiles/small-saas/artifacts/tenant.acme.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Acme Tenant
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/organization
|
||||||
|
- type: represented_by
|
||||||
|
target: small-saas/user/ada-admin
|
||||||
|
- type: isolated_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- id: small-saas/tenant/globex
|
||||||
|
path: profiles/small-saas/artifacts/tenant.globex.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Globex Tenant
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/organization
|
||||||
|
- type: isolated_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- id: small-saas/user/ada-admin
|
||||||
|
path: profiles/small-saas/artifacts/user.ada-admin.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Ada Admin
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/organization
|
||||||
|
- type: uses
|
||||||
|
target: model/access-control
|
||||||
|
- type: member_of
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- type: has_access_under
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- type: access_evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
|
- id: small-saas/team/platform
|
||||||
|
path: profiles/small-saas/artifacts/team.platform.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Platform Team
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/organization
|
||||||
|
- id: small-saas/dataset/subscription-ledger
|
||||||
|
path: profiles/small-saas/artifacts/dataset.subscription-ledger.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Subscription Ledger Dataset
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/data
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: partitioned_for
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: partitioned_for
|
||||||
|
target: small-saas/tenant/globex
|
||||||
|
- type: governed_by
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- id: small-saas/deployment/production
|
||||||
|
path: profiles/small-saas/artifacts/deployment.production.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Production Deployment
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/devsecops
|
||||||
|
- type: uses
|
||||||
|
target: model/network
|
||||||
|
- type: deploys
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: separates
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: separates
|
||||||
|
target: small-saas/tenant/globex
|
||||||
|
- type: implements
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- id: small-saas/task/onboard-tenant
|
||||||
|
path: profiles/small-saas/artifacts/task.onboard-tenant.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Onboard Tenant
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/task
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- type: changes
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: governed_by
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- id: small-saas/policy/tenant-isolation
|
||||||
|
path: profiles/small-saas/artifacts/policy.tenant-isolation.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Tenant Isolation Policy
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/governance
|
||||||
|
- type: requires
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
|
- id: small-saas/control/namespace-per-tenant
|
||||||
|
path: profiles/small-saas/artifacts/control.namespace-per-tenant.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Namespace Per Tenant Control
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/security
|
||||||
|
- type: uses
|
||||||
|
target: standard/caring
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
|
- id: small-saas/evidence/access-review-2026-05
|
||||||
|
path: profiles/small-saas/artifacts/evidence.access-review-2026-05.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Access Review 2026-05
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/observability
|
||||||
|
- id: small-saas/incident/cross-tenant-access-attempt
|
||||||
|
path: profiles/small-saas/artifacts/incident.cross-tenant-access-attempt.yaml
|
||||||
|
kind: profile-artifact
|
||||||
|
title: Cross-Tenant Access Attempt
|
||||||
|
provenance:
|
||||||
|
profile: small-saas
|
||||||
|
placement_workplan: ITC-WP-0004
|
||||||
|
relationships:
|
||||||
|
- type: instantiates
|
||||||
|
target: profile/small-saas
|
||||||
|
- type: uses
|
||||||
|
target: model/security
|
||||||
|
- type: constrained_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
|
|||||||
14
infospace/examples/small-saas/README.md
Normal file
14
infospace/examples/small-saas/README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Small SaaS Example
|
||||||
|
|
||||||
|
This example is the first executable profile proof for InfoTechCanon. It
|
||||||
|
models a compact tenant-aware SaaS service with ownership, tenants, users,
|
||||||
|
access grants, data partitioning, deployment, governance policy, evidence, and
|
||||||
|
incident handling.
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile inspect small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile validate small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile graph small-saas
|
||||||
|
```
|
||||||
29
infospace/examples/small-saas/demo-commands.yaml
Normal file
29
infospace/examples/small-saas/demo-commands.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
profile: small-saas
|
||||||
|
commands:
|
||||||
|
inspect:
|
||||||
|
argv:
|
||||||
|
- PYTHONPATH=src
|
||||||
|
- python3
|
||||||
|
- -m
|
||||||
|
- info_tech_canon
|
||||||
|
- profile
|
||||||
|
- inspect
|
||||||
|
- small-saas
|
||||||
|
validate:
|
||||||
|
argv:
|
||||||
|
- PYTHONPATH=src
|
||||||
|
- python3
|
||||||
|
- -m
|
||||||
|
- info_tech_canon
|
||||||
|
- profile
|
||||||
|
- validate
|
||||||
|
- small-saas
|
||||||
|
graph:
|
||||||
|
argv:
|
||||||
|
- PYTHONPATH=src
|
||||||
|
- python3
|
||||||
|
- -m
|
||||||
|
- info_tech_canon
|
||||||
|
- profile
|
||||||
|
- graph
|
||||||
|
- small-saas
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
root: infospace
|
root: infospace
|
||||||
file_count: 50
|
file_count: 67
|
||||||
files:
|
files:
|
||||||
- path: README.md
|
- path: README.md
|
||||||
directory: .
|
directory: .
|
||||||
@@ -19,6 +19,12 @@ files:
|
|||||||
- path: examples/README.md
|
- path: examples/README.md
|
||||||
directory: examples
|
directory: examples
|
||||||
name: README.md
|
name: README.md
|
||||||
|
- path: examples/small-saas/README.md
|
||||||
|
directory: examples/small-saas
|
||||||
|
name: README.md
|
||||||
|
- path: examples/small-saas/demo-commands.yaml
|
||||||
|
directory: examples/small-saas
|
||||||
|
name: demo-commands.yaml
|
||||||
- path: indexes/README.md
|
- path: indexes/README.md
|
||||||
directory: indexes
|
directory: indexes
|
||||||
name: README.md
|
name: README.md
|
||||||
@@ -82,9 +88,54 @@ files:
|
|||||||
- path: profiles/README.md
|
- path: profiles/README.md
|
||||||
directory: profiles
|
directory: profiles
|
||||||
name: README.md
|
name: README.md
|
||||||
|
- path: profiles/small-saas/artifacts/control.namespace-per-tenant.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: control.namespace-per-tenant.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/dataset.subscription-ledger.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: dataset.subscription-ledger.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/deployment.production.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: deployment.production.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/evidence.access-review-2026-05.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: evidence.access-review-2026-05.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/incident.cross-tenant-access-attempt.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: incident.cross-tenant-access-attempt.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/policy.tenant-isolation.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: policy.tenant-isolation.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/service.billing-portal.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: service.billing-portal.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/system.billing-system.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: system.billing-system.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/task.onboard-tenant.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: task.onboard-tenant.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/team.platform.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: team.platform.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/tenant.acme.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: tenant.acme.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/tenant.globex.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: tenant.globex.yaml
|
||||||
|
- path: profiles/small-saas/artifacts/user.ada-admin.yaml
|
||||||
|
directory: profiles/small-saas/artifacts
|
||||||
|
name: user.ada-admin.yaml
|
||||||
|
- path: profiles/small-saas/profile.yaml
|
||||||
|
directory: profiles/small-saas
|
||||||
|
name: profile.yaml
|
||||||
- path: reports/scaffold-placement.md
|
- path: reports/scaffold-placement.md
|
||||||
directory: reports
|
directory: reports
|
||||||
name: scaffold-placement.md
|
name: scaffold-placement.md
|
||||||
|
- path: reports/small-saas-profile-proof.md
|
||||||
|
directory: reports
|
||||||
|
name: small-saas-profile-proof.md
|
||||||
- path: schemas/README.md
|
- path: schemas/README.md
|
||||||
directory: schemas
|
directory: schemas
|
||||||
name: README.md
|
name: README.md
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
concept_count: 30
|
concept_count: 44
|
||||||
concepts:
|
concepts:
|
||||||
- concept: InfoTechCanon Core
|
- concept: InfoTechCanon Core
|
||||||
owner: kernel/itc-core
|
owner: kernel/itc-core
|
||||||
@@ -52,6 +52,62 @@ concepts:
|
|||||||
owner: model/task
|
owner: model/task
|
||||||
path: models/task/InfoTechCanonTaskModel.md
|
path: models/task/InfoTechCanonTaskModel.md
|
||||||
source: artifact_title
|
source: artifact_title
|
||||||
|
- concept: Small SaaS System Profile
|
||||||
|
owner: profile/small-saas
|
||||||
|
path: profiles/small-saas/profile.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Namespace Per Tenant Control
|
||||||
|
owner: small-saas/control/namespace-per-tenant
|
||||||
|
path: profiles/small-saas/artifacts/control.namespace-per-tenant.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Subscription Ledger Dataset
|
||||||
|
owner: small-saas/dataset/subscription-ledger
|
||||||
|
path: profiles/small-saas/artifacts/dataset.subscription-ledger.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Production Deployment
|
||||||
|
owner: small-saas/deployment/production
|
||||||
|
path: profiles/small-saas/artifacts/deployment.production.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Access Review 2026-05
|
||||||
|
owner: small-saas/evidence/access-review-2026-05
|
||||||
|
path: profiles/small-saas/artifacts/evidence.access-review-2026-05.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Cross-Tenant Access Attempt
|
||||||
|
owner: small-saas/incident/cross-tenant-access-attempt
|
||||||
|
path: profiles/small-saas/artifacts/incident.cross-tenant-access-attempt.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Tenant Isolation Policy
|
||||||
|
owner: small-saas/policy/tenant-isolation
|
||||||
|
path: profiles/small-saas/artifacts/policy.tenant-isolation.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Billing Portal Service
|
||||||
|
owner: small-saas/service/billing-portal
|
||||||
|
path: profiles/small-saas/artifacts/service.billing-portal.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Small SaaS Billing System
|
||||||
|
owner: small-saas/system/billing-system
|
||||||
|
path: profiles/small-saas/artifacts/system.billing-system.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Onboard Tenant
|
||||||
|
owner: small-saas/task/onboard-tenant
|
||||||
|
path: profiles/small-saas/artifacts/task.onboard-tenant.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Platform Team
|
||||||
|
owner: small-saas/team/platform
|
||||||
|
path: profiles/small-saas/artifacts/team.platform.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Acme Tenant
|
||||||
|
owner: small-saas/tenant/acme
|
||||||
|
path: profiles/small-saas/artifacts/tenant.acme.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Globex Tenant
|
||||||
|
owner: small-saas/tenant/globex
|
||||||
|
path: profiles/small-saas/artifacts/tenant.globex.yaml
|
||||||
|
source: artifact_title
|
||||||
|
- concept: Ada Admin
|
||||||
|
owner: small-saas/user/ada-admin
|
||||||
|
path: profiles/small-saas/artifacts/user.ada-admin.yaml
|
||||||
|
source: artifact_title
|
||||||
- concept: InfoTechCanon CARING Access Governance Standard
|
- concept: InfoTechCanon CARING Access Governance Standard
|
||||||
owner: standard/caring
|
owner: standard/caring
|
||||||
path: standards/caring/InfoTechCanonCaringAccessGovernanceStandard.md
|
path: standards/caring/InfoTechCanonCaringAccessGovernanceStandard.md
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ artifacts:
|
|||||||
- model/organization
|
- model/organization
|
||||||
- model/security
|
- model/security
|
||||||
- model/task
|
- model/task
|
||||||
|
- profile/small-saas
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
- small-saas/dataset/subscription-ledger
|
||||||
|
- small-saas/deployment/production
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
- small-saas/incident/cross-tenant-access-attempt
|
||||||
|
- small-saas/policy/tenant-isolation
|
||||||
|
- small-saas/service/billing-portal
|
||||||
|
- small-saas/system/billing-system
|
||||||
|
- small-saas/task/onboard-tenant
|
||||||
|
- small-saas/team/platform
|
||||||
|
- small-saas/tenant/acme
|
||||||
|
- small-saas/tenant/globex
|
||||||
|
- small-saas/user/ada-admin
|
||||||
- standard/caring
|
- standard/caring
|
||||||
- standard/tagging
|
- standard/tagging
|
||||||
rows:
|
rows:
|
||||||
@@ -105,6 +119,170 @@ rows:
|
|||||||
targets:
|
targets:
|
||||||
kernel/itc-core:
|
kernel/itc-core:
|
||||||
- conforms_to
|
- conforms_to
|
||||||
|
- artifact: profile/small-saas
|
||||||
|
targets:
|
||||||
|
kernel/itc-core:
|
||||||
|
- conforms_to
|
||||||
|
model/access-control:
|
||||||
|
- requires
|
||||||
|
model/data:
|
||||||
|
- requires
|
||||||
|
model/devsecops:
|
||||||
|
- requires
|
||||||
|
model/governance:
|
||||||
|
- requires
|
||||||
|
model/landscape:
|
||||||
|
- requires
|
||||||
|
model/network:
|
||||||
|
- requires
|
||||||
|
model/observability:
|
||||||
|
- requires
|
||||||
|
model/organization:
|
||||||
|
- requires
|
||||||
|
model/security:
|
||||||
|
- requires
|
||||||
|
model/task:
|
||||||
|
- requires
|
||||||
|
standard/caring:
|
||||||
|
- requires
|
||||||
|
standard/tagging:
|
||||||
|
- requires
|
||||||
|
- artifact: small-saas/control/namespace-per-tenant
|
||||||
|
targets:
|
||||||
|
model/security:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/evidence/access-review-2026-05:
|
||||||
|
- evidenced_by
|
||||||
|
standard/caring:
|
||||||
|
- uses
|
||||||
|
- artifact: small-saas/dataset/subscription-ledger
|
||||||
|
targets:
|
||||||
|
model/data:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/policy/tenant-isolation:
|
||||||
|
- governed_by
|
||||||
|
small-saas/service/billing-portal:
|
||||||
|
- owned_by
|
||||||
|
small-saas/tenant/acme:
|
||||||
|
- partitioned_for
|
||||||
|
small-saas/tenant/globex:
|
||||||
|
- partitioned_for
|
||||||
|
- artifact: small-saas/deployment/production
|
||||||
|
targets:
|
||||||
|
model/devsecops:
|
||||||
|
- uses
|
||||||
|
model/network:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/control/namespace-per-tenant:
|
||||||
|
- implements
|
||||||
|
small-saas/service/billing-portal:
|
||||||
|
- deploys
|
||||||
|
small-saas/tenant/acme:
|
||||||
|
- separates
|
||||||
|
small-saas/tenant/globex:
|
||||||
|
- separates
|
||||||
|
- artifact: small-saas/evidence/access-review-2026-05
|
||||||
|
targets:
|
||||||
|
model/observability:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
- artifact: small-saas/incident/cross-tenant-access-attempt
|
||||||
|
targets:
|
||||||
|
model/security:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/control/namespace-per-tenant:
|
||||||
|
- constrained_by
|
||||||
|
small-saas/evidence/access-review-2026-05:
|
||||||
|
- evidenced_by
|
||||||
|
- artifact: small-saas/policy/tenant-isolation
|
||||||
|
targets:
|
||||||
|
model/governance:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/control/namespace-per-tenant:
|
||||||
|
- requires
|
||||||
|
small-saas/evidence/access-review-2026-05:
|
||||||
|
- evidenced_by
|
||||||
|
- artifact: small-saas/service/billing-portal
|
||||||
|
targets:
|
||||||
|
model/landscape:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/system/billing-system:
|
||||||
|
- part_of
|
||||||
|
small-saas/team/platform:
|
||||||
|
- owned_by
|
||||||
|
- artifact: small-saas/system/billing-system
|
||||||
|
targets:
|
||||||
|
model/landscape:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/tenant/acme:
|
||||||
|
- serves
|
||||||
|
small-saas/tenant/globex:
|
||||||
|
- serves
|
||||||
|
- artifact: small-saas/task/onboard-tenant
|
||||||
|
targets:
|
||||||
|
model/task:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/policy/tenant-isolation:
|
||||||
|
- governed_by
|
||||||
|
small-saas/team/platform:
|
||||||
|
- owned_by
|
||||||
|
small-saas/tenant/acme:
|
||||||
|
- changes
|
||||||
|
- artifact: small-saas/team/platform
|
||||||
|
targets:
|
||||||
|
model/organization:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
- artifact: small-saas/tenant/acme
|
||||||
|
targets:
|
||||||
|
model/organization:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/control/namespace-per-tenant:
|
||||||
|
- isolated_by
|
||||||
|
small-saas/user/ada-admin:
|
||||||
|
- represented_by
|
||||||
|
- artifact: small-saas/tenant/globex
|
||||||
|
targets:
|
||||||
|
model/organization:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/control/namespace-per-tenant:
|
||||||
|
- isolated_by
|
||||||
|
- artifact: small-saas/user/ada-admin
|
||||||
|
targets:
|
||||||
|
model/access-control:
|
||||||
|
- uses
|
||||||
|
model/organization:
|
||||||
|
- uses
|
||||||
|
profile/small-saas:
|
||||||
|
- instantiates
|
||||||
|
small-saas/evidence/access-review-2026-05:
|
||||||
|
- access_evidenced_by
|
||||||
|
small-saas/policy/tenant-isolation:
|
||||||
|
- has_access_under
|
||||||
|
small-saas/team/platform:
|
||||||
|
- member_of
|
||||||
- artifact: standard/caring
|
- artifact: standard/caring
|
||||||
targets:
|
targets:
|
||||||
kernel/itc-core:
|
kernel/itc-core:
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
id: small-saas/control/namespace-per-tenant
|
||||||
|
kind: control
|
||||||
|
title: Namespace Per Tenant Control
|
||||||
|
profile: small-saas
|
||||||
|
policy_id: small-saas/policy/tenant-isolation
|
||||||
|
claim: Every production tenant has a distinct runtime namespace and data partition.
|
||||||
|
control_type: preventive
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: satisfies
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
id: small-saas/dataset/subscription-ledger
|
||||||
|
kind: dataset
|
||||||
|
title: Subscription Ledger Dataset
|
||||||
|
profile: small-saas
|
||||||
|
owner_service: small-saas/service/billing-portal
|
||||||
|
classification: customer-confidential
|
||||||
|
tenant_scope: per-tenant
|
||||||
|
tenant_ids:
|
||||||
|
- small-saas/tenant/acme
|
||||||
|
- small-saas/tenant/globex
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: partitioned_for
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: partitioned_for
|
||||||
|
target: small-saas/tenant/globex
|
||||||
|
- type: governed_by
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
id: small-saas/deployment/production
|
||||||
|
kind: deployment
|
||||||
|
title: Production Deployment
|
||||||
|
profile: small-saas
|
||||||
|
service_id: small-saas/service/billing-portal
|
||||||
|
environment: production
|
||||||
|
namespace_strategy: namespace-per-tenant
|
||||||
|
tenant_namespaces:
|
||||||
|
small-saas/tenant/acme: tenant-acme
|
||||||
|
small-saas/tenant/globex: tenant-globex
|
||||||
|
network_exposure: public-ingress-authenticated
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: deploys
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: separates
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: separates
|
||||||
|
target: small-saas/tenant/globex
|
||||||
|
- type: implements
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
id: small-saas/evidence/access-review-2026-05
|
||||||
|
kind: evidence
|
||||||
|
title: Access Review 2026-05
|
||||||
|
profile: small-saas
|
||||||
|
evidence_type: review-record
|
||||||
|
date: "2026-05-23"
|
||||||
|
supports:
|
||||||
|
- small-saas/service/billing-portal
|
||||||
|
- small-saas/policy/tenant-isolation
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
- small-saas/incident/cross-tenant-access-attempt
|
||||||
|
relationships:
|
||||||
|
- type: supports
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- type: supports
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- type: supports
|
||||||
|
target: small-saas/incident/cross-tenant-access-attempt
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
id: small-saas/incident/cross-tenant-access-attempt
|
||||||
|
kind: incident
|
||||||
|
title: Cross-Tenant Access Attempt
|
||||||
|
profile: small-saas
|
||||||
|
status: resolved
|
||||||
|
tenant_id: small-saas/tenant/acme
|
||||||
|
control_ids:
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: constrained_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
id: small-saas/policy/tenant-isolation
|
||||||
|
kind: policy
|
||||||
|
title: Tenant Isolation Policy
|
||||||
|
profile: small-saas
|
||||||
|
scope: service-and-data-plane
|
||||||
|
statement: Tenant requests, namespaces, access grants, and data partitions must not cross tenant boundaries.
|
||||||
|
control_ids:
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: requires
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
|
- type: evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
id: small-saas/service/billing-portal
|
||||||
|
kind: service
|
||||||
|
title: Billing Portal Service
|
||||||
|
profile: small-saas
|
||||||
|
system_id: small-saas/system/billing-system
|
||||||
|
owner_team: small-saas/team/platform
|
||||||
|
runtime_deployment: small-saas/deployment/production
|
||||||
|
datasets:
|
||||||
|
- small-saas/dataset/subscription-ledger
|
||||||
|
controls:
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
policies:
|
||||||
|
- small-saas/policy/tenant-isolation
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: part_of
|
||||||
|
target: small-saas/system/billing-system
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- type: stores
|
||||||
|
target: small-saas/dataset/subscription-ledger
|
||||||
|
- type: deployed_as
|
||||||
|
target: small-saas/deployment/production
|
||||||
|
- type: governed_by
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- type: protected_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
id: small-saas/system/billing-system
|
||||||
|
kind: system
|
||||||
|
title: Small SaaS Billing System
|
||||||
|
profile: small-saas
|
||||||
|
service_ids:
|
||||||
|
- small-saas/service/billing-portal
|
||||||
|
tenant_ids:
|
||||||
|
- small-saas/tenant/acme
|
||||||
|
- small-saas/tenant/globex
|
||||||
|
owner_team: small-saas/team/platform
|
||||||
|
relationships:
|
||||||
|
- type: includes
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: serves
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: serves
|
||||||
|
target: small-saas/tenant/globex
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
id: small-saas/task/onboard-tenant
|
||||||
|
kind: task
|
||||||
|
title: Onboard Tenant
|
||||||
|
profile: small-saas
|
||||||
|
owner_team: small-saas/team/platform
|
||||||
|
status: ready
|
||||||
|
target_tenant: small-saas/tenant/acme
|
||||||
|
evidence_ids:
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: owned_by
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- type: changes
|
||||||
|
target: small-saas/tenant/acme
|
||||||
|
- type: governed_by
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
14
infospace/profiles/small-saas/artifacts/team.platform.yaml
Normal file
14
infospace/profiles/small-saas/artifacts/team.platform.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
id: small-saas/team/platform
|
||||||
|
kind: team
|
||||||
|
title: Platform Team
|
||||||
|
profile: small-saas
|
||||||
|
owner_user: small-saas/user/ada-admin
|
||||||
|
responsibilities:
|
||||||
|
- service ownership
|
||||||
|
- tenant onboarding
|
||||||
|
- access review
|
||||||
|
relationships:
|
||||||
|
- type: owns
|
||||||
|
target: small-saas/service/billing-portal
|
||||||
|
- type: performs
|
||||||
|
target: small-saas/task/onboard-tenant
|
||||||
12
infospace/profiles/small-saas/artifacts/tenant.acme.yaml
Normal file
12
infospace/profiles/small-saas/artifacts/tenant.acme.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
id: small-saas/tenant/acme
|
||||||
|
kind: tenant
|
||||||
|
title: Acme Tenant
|
||||||
|
profile: small-saas
|
||||||
|
namespace: tenant-acme
|
||||||
|
data_partition: subscription-ledger:tenant=acme
|
||||||
|
primary_user: small-saas/user/ada-admin
|
||||||
|
relationships:
|
||||||
|
- type: represented_by
|
||||||
|
target: small-saas/user/ada-admin
|
||||||
|
- type: isolated_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
id: small-saas/tenant/globex
|
||||||
|
kind: tenant
|
||||||
|
title: Globex Tenant
|
||||||
|
profile: small-saas
|
||||||
|
namespace: tenant-globex
|
||||||
|
data_partition: subscription-ledger:tenant=globex
|
||||||
|
relationships:
|
||||||
|
- type: isolated_by
|
||||||
|
target: small-saas/control/namespace-per-tenant
|
||||||
19
infospace/profiles/small-saas/artifacts/user.ada-admin.yaml
Normal file
19
infospace/profiles/small-saas/artifacts/user.ada-admin.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
id: small-saas/user/ada-admin
|
||||||
|
kind: user
|
||||||
|
title: Ada Admin
|
||||||
|
profile: small-saas
|
||||||
|
person_type: human
|
||||||
|
teams:
|
||||||
|
- small-saas/team/platform
|
||||||
|
access_grants:
|
||||||
|
- role: tenant-admin
|
||||||
|
tenant_id: small-saas/tenant/acme
|
||||||
|
policy_id: small-saas/policy/tenant-isolation
|
||||||
|
evidence_id: small-saas/evidence/access-review-2026-05
|
||||||
|
relationships:
|
||||||
|
- type: member_of
|
||||||
|
target: small-saas/team/platform
|
||||||
|
- type: has_access_under
|
||||||
|
target: small-saas/policy/tenant-isolation
|
||||||
|
- type: access_evidenced_by
|
||||||
|
target: small-saas/evidence/access-review-2026-05
|
||||||
112
infospace/profiles/small-saas/profile.yaml
Normal file
112
infospace/profiles/small-saas/profile.yaml
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
id: small-saas
|
||||||
|
kind: profile
|
||||||
|
profile: small-saas
|
||||||
|
title: Small SaaS System Profile
|
||||||
|
scope: A compact tenant-aware SaaS service with users, teams, data, access, deployment, governance evidence, and incident handling.
|
||||||
|
status: proof
|
||||||
|
conformance_level: profile-proof
|
||||||
|
assumptions:
|
||||||
|
- The SaaS product has a single service boundary and two example tenants.
|
||||||
|
- Tenants are separated by namespace and data partitioning claims.
|
||||||
|
- User management is represented through users, teams, access grants, policies, controls, and evidence.
|
||||||
|
- Runtime concerns are represented by one production deployment.
|
||||||
|
required_standards:
|
||||||
|
- kernel/itc-core
|
||||||
|
- model/landscape
|
||||||
|
- model/organization
|
||||||
|
- model/governance
|
||||||
|
- model/task
|
||||||
|
- model/access-control
|
||||||
|
- model/security
|
||||||
|
- model/data
|
||||||
|
- model/devsecops
|
||||||
|
- model/network
|
||||||
|
- model/observability
|
||||||
|
- standard/tagging
|
||||||
|
- standard/caring
|
||||||
|
required_concepts:
|
||||||
|
service:
|
||||||
|
status: required
|
||||||
|
model: model/landscape
|
||||||
|
system:
|
||||||
|
status: required
|
||||||
|
model: model/landscape
|
||||||
|
tenant:
|
||||||
|
status: required
|
||||||
|
model: model/organization
|
||||||
|
user:
|
||||||
|
status: required
|
||||||
|
model: model/organization
|
||||||
|
team:
|
||||||
|
status: required
|
||||||
|
model: model/organization
|
||||||
|
dataset:
|
||||||
|
status: required
|
||||||
|
model: model/data
|
||||||
|
deployment:
|
||||||
|
status: required
|
||||||
|
model: model/devsecops
|
||||||
|
task:
|
||||||
|
status: required
|
||||||
|
model: model/task
|
||||||
|
policy:
|
||||||
|
status: required
|
||||||
|
model: model/governance
|
||||||
|
control:
|
||||||
|
status: required
|
||||||
|
model: model/security
|
||||||
|
evidence:
|
||||||
|
status: required
|
||||||
|
model: model/observability
|
||||||
|
incident:
|
||||||
|
status: required
|
||||||
|
model: model/security
|
||||||
|
optional_concepts:
|
||||||
|
billing-plan:
|
||||||
|
status: optional
|
||||||
|
model: model/data
|
||||||
|
notification:
|
||||||
|
status: optional
|
||||||
|
model: model/observability
|
||||||
|
out_of_scope:
|
||||||
|
- multi-region disaster recovery
|
||||||
|
- tenant-managed encryption keys
|
||||||
|
- marketplace billing integrations
|
||||||
|
artifact_ids:
|
||||||
|
- profile/small-saas
|
||||||
|
- small-saas/service/billing-portal
|
||||||
|
- small-saas/system/billing-system
|
||||||
|
- small-saas/tenant/acme
|
||||||
|
- small-saas/tenant/globex
|
||||||
|
- small-saas/user/ada-admin
|
||||||
|
- small-saas/team/platform
|
||||||
|
- small-saas/dataset/subscription-ledger
|
||||||
|
- small-saas/deployment/production
|
||||||
|
- small-saas/task/onboard-tenant
|
||||||
|
- small-saas/policy/tenant-isolation
|
||||||
|
- small-saas/control/namespace-per-tenant
|
||||||
|
- small-saas/evidence/access-review-2026-05
|
||||||
|
- small-saas/incident/cross-tenant-access-attempt
|
||||||
|
validation_rules:
|
||||||
|
required_artifact_kinds:
|
||||||
|
- service
|
||||||
|
- system
|
||||||
|
- tenant
|
||||||
|
- user
|
||||||
|
- team
|
||||||
|
- dataset
|
||||||
|
- deployment
|
||||||
|
- task
|
||||||
|
- policy
|
||||||
|
- control
|
||||||
|
- evidence
|
||||||
|
- incident
|
||||||
|
service_ownership: required
|
||||||
|
tenant_namespace_separation: required
|
||||||
|
user_management_trace: required
|
||||||
|
access_control_trace: required
|
||||||
|
governance_evidence: required
|
||||||
|
demo_commands:
|
||||||
|
- PYTHONPATH=src python3 -m info_tech_canon profile inspect small-saas
|
||||||
|
- PYTHONPATH=src python3 -m info_tech_canon profile validate small-saas
|
||||||
|
- PYTHONPATH=src python3 -m info_tech_canon profile graph small-saas
|
||||||
44
infospace/reports/small-saas-profile-proof.md
Normal file
44
infospace/reports/small-saas-profile-proof.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Small SaaS Profile Proof
|
||||||
|
|
||||||
|
**Workplan:** ITC-WP-0004
|
||||||
|
**Profile:** `small-saas`
|
||||||
|
**Status:** executable proof
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The `small-saas` profile demonstrates that the canon can guide a practical
|
||||||
|
service shape before the larger CARING benchmark exists. It connects service
|
||||||
|
ownership, users, tenants, access, data, runtime, deployment, governance,
|
||||||
|
observability, and task tracking.
|
||||||
|
|
||||||
|
## Validation Surface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile inspect small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile validate small-saas
|
||||||
|
PYTHONPATH=src python3 -m info_tech_canon profile graph small-saas
|
||||||
|
```
|
||||||
|
|
||||||
|
## Graph Slice
|
||||||
|
|
||||||
|
The profile graph includes the profile artifact, the small SaaS example
|
||||||
|
artifacts, and their direct canon anchors.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
profile_small_saas["profile/small-saas"] -->|conforms_to| core["kernel/itc-core"]
|
||||||
|
service["small-saas/service/billing-portal"] -->|part_of| system["small-saas/system/billing-system"]
|
||||||
|
service -->|owned_by| team["small-saas/team/platform"]
|
||||||
|
service -->|governed_by| policy["small-saas/policy/tenant-isolation"]
|
||||||
|
service -->|protected_by| control["small-saas/control/namespace-per-tenant"]
|
||||||
|
deployment["small-saas/deployment/production"] -->|separates| acme["small-saas/tenant/acme"]
|
||||||
|
deployment -->|separates| globex["small-saas/tenant/globex"]
|
||||||
|
user["small-saas/user/ada-admin"] -->|has_access_under| policy
|
||||||
|
policy -->|evidenced_by| evidence["small-saas/evidence/access-review-2026-05"]
|
||||||
|
incident["small-saas/incident/cross-tenant-access-attempt"] -->|constrained_by| control
|
||||||
|
```
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
The proof is suitable as a baseline for the upcoming `user-engine` evaluation
|
||||||
|
pack and for `railiance-fabric` entity/edge capture comparisons.
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"details": {
|
"details": {
|
||||||
"artifact_count": 15,
|
"artifact_count": 29,
|
||||||
"relationship_count": 45
|
"relationship_count": 113
|
||||||
},
|
},
|
||||||
"errors": [],
|
"errors": [],
|
||||||
"metrics": {
|
"metrics": {
|
||||||
"coherence_components": 1.0,
|
"coherence_components": 1.0,
|
||||||
"consistency_cycles": 0.0,
|
"consistency_cycles": 0.0,
|
||||||
"coverage_ratio": 1.0,
|
"coverage_ratio": 1.0,
|
||||||
"granularity_entropy": 1.103307408607834,
|
"granularity_entropy": 1.7490339557881076,
|
||||||
"redundancy_ratio": 0.0
|
"redundancy_ratio": 0.0
|
||||||
},
|
},
|
||||||
"ok": true,
|
"ok": true,
|
||||||
@@ -17,10 +17,6 @@
|
|||||||
"code": "missing_optional_concepts_dir",
|
"code": "missing_optional_concepts_dir",
|
||||||
"path": "infospace/concepts"
|
"path": "infospace/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"code": "empty_optional_collection",
|
|
||||||
"path": "infospace/profiles"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"code": "empty_optional_collection",
|
"code": "empty_optional_collection",
|
||||||
"path": "infospace/patterns"
|
"path": "infospace/patterns"
|
||||||
@@ -32,10 +28,6 @@
|
|||||||
{
|
{
|
||||||
"code": "empty_optional_collection",
|
"code": "empty_optional_collection",
|
||||||
"path": "infospace/assimilation"
|
"path": "infospace/assimilation"
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": "empty_optional_collection",
|
|
||||||
"path": "infospace/examples"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# By Concept
|
# By Concept
|
||||||
|
|
||||||
Concept count: **30**
|
Concept count: **44**
|
||||||
|
|
||||||
| Concept | Owner | Source |
|
| Concept | Owner | Source |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
@@ -19,6 +19,20 @@ Concept count: **30**
|
|||||||
| InfoTechCanon Organization Model | `model/organization` | `artifact_title` |
|
| InfoTechCanon Organization Model | `model/organization` | `artifact_title` |
|
||||||
| InfoTechCanon Security Model | `model/security` | `artifact_title` |
|
| InfoTechCanon Security Model | `model/security` | `artifact_title` |
|
||||||
| InfoTechCanon Task Model | `model/task` | `artifact_title` |
|
| InfoTechCanon Task Model | `model/task` | `artifact_title` |
|
||||||
|
| Small SaaS System Profile | `profile/small-saas` | `artifact_title` |
|
||||||
|
| Namespace Per Tenant Control | `small-saas/control/namespace-per-tenant` | `artifact_title` |
|
||||||
|
| Subscription Ledger Dataset | `small-saas/dataset/subscription-ledger` | `artifact_title` |
|
||||||
|
| Production Deployment | `small-saas/deployment/production` | `artifact_title` |
|
||||||
|
| Access Review 2026-05 | `small-saas/evidence/access-review-2026-05` | `artifact_title` |
|
||||||
|
| Cross-Tenant Access Attempt | `small-saas/incident/cross-tenant-access-attempt` | `artifact_title` |
|
||||||
|
| Tenant Isolation Policy | `small-saas/policy/tenant-isolation` | `artifact_title` |
|
||||||
|
| Billing Portal Service | `small-saas/service/billing-portal` | `artifact_title` |
|
||||||
|
| Small SaaS Billing System | `small-saas/system/billing-system` | `artifact_title` |
|
||||||
|
| Onboard Tenant | `small-saas/task/onboard-tenant` | `artifact_title` |
|
||||||
|
| Platform Team | `small-saas/team/platform` | `artifact_title` |
|
||||||
|
| Acme Tenant | `small-saas/tenant/acme` | `artifact_title` |
|
||||||
|
| Globex Tenant | `small-saas/tenant/globex` | `artifact_title` |
|
||||||
|
| Ada Admin | `small-saas/user/ada-admin` | `artifact_title` |
|
||||||
| InfoTechCanon CARING Access Governance Standard | `standard/caring` | `artifact_title` |
|
| InfoTechCanon CARING Access Governance Standard | `standard/caring` | `artifact_title` |
|
||||||
| CARINGAccessDescriptor | `standard/caring` | `frontmatter.owned_concepts` |
|
| CARINGAccessDescriptor | `standard/caring` | `frontmatter.owned_concepts` |
|
||||||
| CARINGCanonicalRole | `standard/caring` | `frontmatter.owned_concepts` |
|
| CARINGCanonicalRole | `standard/caring` | `frontmatter.owned_concepts` |
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
- `model/organization` via `conforms_to`
|
- `model/organization` via `conforms_to`
|
||||||
- `model/security` via `conforms_to`
|
- `model/security` via `conforms_to`
|
||||||
- `model/task` via `conforms_to`
|
- `model/task` via `conforms_to`
|
||||||
|
- `profile/small-saas` via `conforms_to`
|
||||||
- `standard/caring` via `conforms_to`
|
- `standard/caring` via `conforms_to`
|
||||||
- `standard/tagging` via `conforms_to`
|
- `standard/tagging` via `conforms_to`
|
||||||
|
|
||||||
@@ -23,16 +24,22 @@
|
|||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
- `model/security` via `uses`
|
- `model/security` via `uses`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/user/ada-admin` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/data`
|
## `model/data`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/devsecops`
|
## `model/devsecops`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/deployment/production` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/governance`
|
## `model/governance`
|
||||||
@@ -40,6 +47,8 @@
|
|||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
- `model/access-control` via `uses`
|
- `model/access-control` via `uses`
|
||||||
- `model/data` via `uses`
|
- `model/data` via `uses`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/policy/tenant-isolation` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/information-space`
|
## `model/information-space`
|
||||||
@@ -49,21 +58,33 @@
|
|||||||
## `model/landscape`
|
## `model/landscape`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/service/billing-portal` via `uses`
|
||||||
|
- `small-saas/system/billing-system` via `uses`
|
||||||
|
|
||||||
## `model/network`
|
## `model/network`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/deployment/production` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/observability`
|
## `model/observability`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/evidence/access-review-2026-05` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/organization`
|
## `model/organization`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
- `model/access-control` via `uses`
|
- `model/access-control` via `uses`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/team/platform` via `uses`
|
||||||
|
- `small-saas/tenant/acme` via `uses`
|
||||||
|
- `small-saas/tenant/globex` via `uses`
|
||||||
|
- `small-saas/user/ada-admin` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/security`
|
## `model/security`
|
||||||
@@ -71,20 +92,97 @@
|
|||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
- `model/devsecops` via `uses`
|
- `model/devsecops` via `uses`
|
||||||
- `model/network` via `uses`
|
- `model/network` via `uses`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/control/namespace-per-tenant` via `uses`
|
||||||
|
- `small-saas/incident/cross-tenant-access-attempt` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|
||||||
## `model/task`
|
## `model/task`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
- `model/observability` via `uses`
|
- `model/observability` via `uses`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/task/onboard-tenant` via `uses`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
- `standard/tagging` via `imports`
|
- `standard/tagging` via `imports`
|
||||||
|
|
||||||
|
## `profile/small-saas`
|
||||||
|
|
||||||
|
- `small-saas/control/namespace-per-tenant` via `instantiates`
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `instantiates`
|
||||||
|
- `small-saas/deployment/production` via `instantiates`
|
||||||
|
- `small-saas/evidence/access-review-2026-05` via `instantiates`
|
||||||
|
- `small-saas/incident/cross-tenant-access-attempt` via `instantiates`
|
||||||
|
- `small-saas/policy/tenant-isolation` via `instantiates`
|
||||||
|
- `small-saas/service/billing-portal` via `instantiates`
|
||||||
|
- `small-saas/system/billing-system` via `instantiates`
|
||||||
|
- `small-saas/task/onboard-tenant` via `instantiates`
|
||||||
|
- `small-saas/team/platform` via `instantiates`
|
||||||
|
- `small-saas/tenant/acme` via `instantiates`
|
||||||
|
- `small-saas/tenant/globex` via `instantiates`
|
||||||
|
- `small-saas/user/ada-admin` via `instantiates`
|
||||||
|
|
||||||
|
## `small-saas/control/namespace-per-tenant`
|
||||||
|
|
||||||
|
- `small-saas/deployment/production` via `implements`
|
||||||
|
- `small-saas/incident/cross-tenant-access-attempt` via `constrained_by`
|
||||||
|
- `small-saas/policy/tenant-isolation` via `requires`
|
||||||
|
- `small-saas/tenant/acme` via `isolated_by`
|
||||||
|
- `small-saas/tenant/globex` via `isolated_by`
|
||||||
|
|
||||||
|
## `small-saas/evidence/access-review-2026-05`
|
||||||
|
|
||||||
|
- `small-saas/control/namespace-per-tenant` via `evidenced_by`
|
||||||
|
- `small-saas/incident/cross-tenant-access-attempt` via `evidenced_by`
|
||||||
|
- `small-saas/policy/tenant-isolation` via `evidenced_by`
|
||||||
|
- `small-saas/user/ada-admin` via `access_evidenced_by`
|
||||||
|
|
||||||
|
## `small-saas/policy/tenant-isolation`
|
||||||
|
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `governed_by`
|
||||||
|
- `small-saas/task/onboard-tenant` via `governed_by`
|
||||||
|
- `small-saas/user/ada-admin` via `has_access_under`
|
||||||
|
|
||||||
|
## `small-saas/service/billing-portal`
|
||||||
|
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `owned_by`
|
||||||
|
- `small-saas/deployment/production` via `deploys`
|
||||||
|
|
||||||
|
## `small-saas/system/billing-system`
|
||||||
|
|
||||||
|
- `small-saas/service/billing-portal` via `part_of`
|
||||||
|
|
||||||
|
## `small-saas/team/platform`
|
||||||
|
|
||||||
|
- `small-saas/service/billing-portal` via `owned_by`
|
||||||
|
- `small-saas/task/onboard-tenant` via `owned_by`
|
||||||
|
- `small-saas/user/ada-admin` via `member_of`
|
||||||
|
|
||||||
|
## `small-saas/tenant/acme`
|
||||||
|
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `partitioned_for`
|
||||||
|
- `small-saas/deployment/production` via `separates`
|
||||||
|
- `small-saas/system/billing-system` via `serves`
|
||||||
|
- `small-saas/task/onboard-tenant` via `changes`
|
||||||
|
|
||||||
|
## `small-saas/tenant/globex`
|
||||||
|
|
||||||
|
- `small-saas/dataset/subscription-ledger` via `partitioned_for`
|
||||||
|
- `small-saas/deployment/production` via `separates`
|
||||||
|
- `small-saas/system/billing-system` via `serves`
|
||||||
|
|
||||||
|
## `small-saas/user/ada-admin`
|
||||||
|
|
||||||
|
- `small-saas/tenant/acme` via `represented_by`
|
||||||
|
|
||||||
## `standard/caring`
|
## `standard/caring`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
|
- `small-saas/control/namespace-per-tenant` via `uses`
|
||||||
|
|
||||||
## `standard/tagging`
|
## `standard/tagging`
|
||||||
|
|
||||||
- `kernel/itc-kernel-map` via `maps`
|
- `kernel/itc-kernel-map` via `maps`
|
||||||
|
- `profile/small-saas` via `requires`
|
||||||
- `standard/caring` via `imports`
|
- `standard/caring` via `imports`
|
||||||
|
|||||||
@@ -2,4 +2,6 @@
|
|||||||
|
|
||||||
# By Profile
|
# By Profile
|
||||||
|
|
||||||
No profiles have been registered yet.
|
## small-saas
|
||||||
|
|
||||||
|
- Path: `profiles/small-saas/profile.yaml`
|
||||||
|
|||||||
@@ -2,20 +2,34 @@
|
|||||||
|
|
||||||
# Import Matrix
|
# Import Matrix
|
||||||
|
|
||||||
| Artifact | `kernel/itc-core` | `kernel/itc-kernel-map` | `model/access-control` | `model/data` | `model/devsecops` | `model/governance` | `model/information-space` | `model/landscape` | `model/network` | `model/observability` | `model/organization` | `model/security` | `model/task` | `standard/caring` | `standard/tagging` |
|
| Artifact | `kernel/itc-core` | `kernel/itc-kernel-map` | `model/access-control` | `model/data` | `model/devsecops` | `model/governance` | `model/information-space` | `model/landscape` | `model/network` | `model/observability` | `model/organization` | `model/security` | `model/task` | `profile/small-saas` | `small-saas/control/namespace-per-tenant` | `small-saas/dataset/subscription-ledger` | `small-saas/deployment/production` | `small-saas/evidence/access-review-2026-05` | `small-saas/incident/cross-tenant-access-attempt` | `small-saas/policy/tenant-isolation` | `small-saas/service/billing-portal` | `small-saas/system/billing-system` | `small-saas/task/onboard-tenant` | `small-saas/team/platform` | `small-saas/tenant/acme` | `small-saas/tenant/globex` | `small-saas/user/ada-admin` | `standard/caring` | `standard/tagging` |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| `kernel/itc-core` | | | | | | | | | | | | | | | |
|
| `kernel/itc-core` | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `kernel/itc-kernel-map` | `maps` | | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` |
|
| `kernel/itc-kernel-map` | `maps` | | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | | | | | | | | | | | | | | | `maps` | `maps` |
|
||||||
| `model/access-control` | `conforms_to` | | | | | `uses` | | | | | `uses` | | | | |
|
| `model/access-control` | `conforms_to` | | | | | `uses` | | | | | `uses` | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/data` | `conforms_to` | | | | | `uses` | | | | | | | | | |
|
| `model/data` | `conforms_to` | | | | | `uses` | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/devsecops` | `conforms_to` | | | | | | | | | | | `uses` | | | |
|
| `model/devsecops` | `conforms_to` | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | | |
|
||||||
| `model/governance` | `conforms_to` | | | | | | | | | | | | | | |
|
| `model/governance` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/information-space` | `conforms_to` | | | | | | | | | | | | | | |
|
| `model/information-space` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/landscape` | `conforms_to` | | | | | | | | | | | | | | |
|
| `model/landscape` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/network` | `conforms_to` | | | | | | | | | | | `uses` | | | |
|
| `model/network` | `conforms_to` | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | | |
|
||||||
| `model/observability` | `conforms_to` | | | | | | | | | | | | `uses` | | |
|
| `model/observability` | `conforms_to` | | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | |
|
||||||
| `model/organization` | `conforms_to` | | | | | | | | | | | | | | |
|
| `model/organization` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/security` | `conforms_to` | | `uses` | | | | | | | | | | | | |
|
| `model/security` | `conforms_to` | | `uses` | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `model/task` | `conforms_to` | | | | | | | | | | | | | | |
|
| `model/task` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
| `standard/caring` | `conforms_to` | | `imports` | `imports` | `imports` | `imports` | | | `imports` | `imports` | `imports` | `imports` | `imports` | | `imports` |
|
| `profile/small-saas` | `conforms_to` | | `requires` | `requires` | `requires` | `requires` | | `requires` | `requires` | `requires` | `requires` | `requires` | `requires` | | | | | | | | | | | | | | | `requires` | `requires` |
|
||||||
| `standard/tagging` | `conforms_to` | | | | | | | | | | | | `imports` | | |
|
| `small-saas/control/namespace-per-tenant` | | | | | | | | | | | | `uses` | | `instantiates` | | | | `evidenced_by` | | | | | | | | | | `uses` | |
|
||||||
|
| `small-saas/dataset/subscription-ledger` | | | | `uses` | | | | | | | | | | `instantiates` | | | | | | `governed_by` | `owned_by` | | | | `partitioned_for` | `partitioned_for` | | | |
|
||||||
|
| `small-saas/deployment/production` | | | | | `uses` | | | | `uses` | | | | | `instantiates` | `implements` | | | | | | `deploys` | | | | `separates` | `separates` | | | |
|
||||||
|
| `small-saas/evidence/access-review-2026-05` | | | | | | | | | | `uses` | | | | `instantiates` | | | | | | | | | | | | | | | |
|
||||||
|
| `small-saas/incident/cross-tenant-access-attempt` | | | | | | | | | | | | `uses` | | `instantiates` | `constrained_by` | | | `evidenced_by` | | | | | | | | | | | |
|
||||||
|
| `small-saas/policy/tenant-isolation` | | | | | | `uses` | | | | | | | | `instantiates` | `requires` | | | `evidenced_by` | | | | | | | | | | | |
|
||||||
|
| `small-saas/service/billing-portal` | | | | | | | | `uses` | | | | | | `instantiates` | | | | | | | | `part_of` | | `owned_by` | | | | | |
|
||||||
|
| `small-saas/system/billing-system` | | | | | | | | `uses` | | | | | | `instantiates` | | | | | | | | | | | `serves` | `serves` | | | |
|
||||||
|
| `small-saas/task/onboard-tenant` | | | | | | | | | | | | | `uses` | `instantiates` | | | | | | `governed_by` | | | | `owned_by` | `changes` | | | | |
|
||||||
|
| `small-saas/team/platform` | | | | | | | | | | | `uses` | | | `instantiates` | | | | | | | | | | | | | | | |
|
||||||
|
| `small-saas/tenant/acme` | | | | | | | | | | | `uses` | | | `instantiates` | `isolated_by` | | | | | | | | | | | | `represented_by` | | |
|
||||||
|
| `small-saas/tenant/globex` | | | | | | | | | | | `uses` | | | `instantiates` | `isolated_by` | | | | | | | | | | | | | | |
|
||||||
|
| `small-saas/user/ada-admin` | | | `uses` | | | | | | | | `uses` | | | `instantiates` | | | | `access_evidenced_by` | | `has_access_under` | | | | `member_of` | | | | | |
|
||||||
|
| `standard/caring` | `conforms_to` | | `imports` | `imports` | `imports` | `imports` | | | `imports` | `imports` | `imports` | `imports` | `imports` | | | | | | | | | | | | | | | | `imports` |
|
||||||
|
| `standard/tagging` | `conforms_to` | | | | | | | | | | | | `imports` | | | | | | | | | | | | | | | | |
|
||||||
|
|||||||
@@ -3,17 +3,37 @@
|
|||||||
# Kernel Overview
|
# Kernel Overview
|
||||||
|
|
||||||
- Infospace: `canon`
|
- Infospace: `canon`
|
||||||
- Artifacts: 15
|
- Artifacts: 29
|
||||||
|
|
||||||
## Artifact Kinds
|
## Artifact Kinds
|
||||||
|
|
||||||
- `kernel`: 2
|
- `kernel`: 2
|
||||||
- `model`: 11
|
- `model`: 11
|
||||||
|
- `profile`: 1
|
||||||
|
- `profile-artifact`: 13
|
||||||
- `standard`: 2
|
- `standard`: 2
|
||||||
|
|
||||||
## Relationship Types
|
## Relationship Types
|
||||||
|
|
||||||
- `conforms_to`: 13
|
- `access_evidenced_by`: 1
|
||||||
|
- `changes`: 1
|
||||||
|
- `conforms_to`: 14
|
||||||
|
- `constrained_by`: 1
|
||||||
|
- `deploys`: 1
|
||||||
|
- `evidenced_by`: 3
|
||||||
|
- `governed_by`: 2
|
||||||
|
- `has_access_under`: 1
|
||||||
|
- `implements`: 1
|
||||||
- `imports`: 11
|
- `imports`: 11
|
||||||
|
- `instantiates`: 13
|
||||||
|
- `isolated_by`: 2
|
||||||
- `maps`: 14
|
- `maps`: 14
|
||||||
- `uses`: 7
|
- `member_of`: 1
|
||||||
|
- `owned_by`: 3
|
||||||
|
- `part_of`: 1
|
||||||
|
- `partitioned_for`: 2
|
||||||
|
- `represented_by`: 1
|
||||||
|
- `requires`: 13
|
||||||
|
- `separates`: 2
|
||||||
|
- `serves`: 2
|
||||||
|
- `uses`: 23
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Repository Tree
|
# Repository Tree
|
||||||
|
|
||||||
File count: **50**
|
File count: **67**
|
||||||
|
|
||||||
- `README.md`
|
- `README.md`
|
||||||
- `agent/README.md`
|
- `agent/README.md`
|
||||||
@@ -10,6 +10,8 @@ File count: **50**
|
|||||||
- `artifacts/index.yaml`
|
- `artifacts/index.yaml`
|
||||||
- `assimilation/README.md`
|
- `assimilation/README.md`
|
||||||
- `examples/README.md`
|
- `examples/README.md`
|
||||||
|
- `examples/small-saas/README.md`
|
||||||
|
- `examples/small-saas/demo-commands.yaml`
|
||||||
- `indexes/README.md`
|
- `indexes/README.md`
|
||||||
- `indexes/artifact-tree.yaml`
|
- `indexes/artifact-tree.yaml`
|
||||||
- `indexes/concept-ownership.yaml`
|
- `indexes/concept-ownership.yaml`
|
||||||
@@ -31,7 +33,22 @@ File count: **50**
|
|||||||
- `models/task/InfoTechCanonTaskModel.md`
|
- `models/task/InfoTechCanonTaskModel.md`
|
||||||
- `patterns/README.md`
|
- `patterns/README.md`
|
||||||
- `profiles/README.md`
|
- `profiles/README.md`
|
||||||
|
- `profiles/small-saas/artifacts/control.namespace-per-tenant.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/dataset.subscription-ledger.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/deployment.production.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/evidence.access-review-2026-05.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/incident.cross-tenant-access-attempt.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/policy.tenant-isolation.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/service.billing-portal.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/system.billing-system.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/task.onboard-tenant.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/team.platform.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/tenant.acme.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/tenant.globex.yaml`
|
||||||
|
- `profiles/small-saas/artifacts/user.ada-admin.yaml`
|
||||||
|
- `profiles/small-saas/profile.yaml`
|
||||||
- `reports/scaffold-placement.md`
|
- `reports/scaffold-placement.md`
|
||||||
|
- `reports/small-saas-profile-proof.md`
|
||||||
- `schemas/README.md`
|
- `schemas/README.md`
|
||||||
- `schemas/agent-brief.schema.yaml`
|
- `schemas/agent-brief.schema.yaml`
|
||||||
- `schemas/assimilation.schema.yaml`
|
- `schemas/assimilation.schema.yaml`
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ from .service import (
|
|||||||
list_models,
|
list_models,
|
||||||
list_standards,
|
list_standards,
|
||||||
list_views,
|
list_views,
|
||||||
|
profile_graph,
|
||||||
profile_inspect,
|
profile_inspect,
|
||||||
|
profile_validate,
|
||||||
read_view,
|
read_view,
|
||||||
validate_canon,
|
validate_canon,
|
||||||
write_validation_report,
|
write_validation_report,
|
||||||
@@ -28,7 +30,9 @@ __all__ = [
|
|||||||
"list_models",
|
"list_models",
|
||||||
"list_standards",
|
"list_standards",
|
||||||
"list_views",
|
"list_views",
|
||||||
|
"profile_graph",
|
||||||
"profile_inspect",
|
"profile_inspect",
|
||||||
|
"profile_validate",
|
||||||
"read_view",
|
"read_view",
|
||||||
"validate_canon",
|
"validate_canon",
|
||||||
"write_validation_report",
|
"write_validation_report",
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ from .service import (
|
|||||||
list_models,
|
list_models,
|
||||||
list_standards,
|
list_standards,
|
||||||
list_views,
|
list_views,
|
||||||
|
profile_graph,
|
||||||
profile_inspect,
|
profile_inspect,
|
||||||
|
profile_validate,
|
||||||
read_view,
|
read_view,
|
||||||
validate_canon,
|
validate_canon,
|
||||||
)
|
)
|
||||||
@@ -96,6 +98,14 @@ def _route(
|
|||||||
if path.startswith("/profiles/") and path.endswith("/inspect"):
|
if path.startswith("/profiles/") and path.endswith("/inspect"):
|
||||||
profile = path.removeprefix("/profiles/").removesuffix("/inspect").strip("/")
|
profile = path.removeprefix("/profiles/").removesuffix("/inspect").strip("/")
|
||||||
return HTTPStatus.OK, profile_inspect(profile, root)
|
return HTTPStatus.OK, profile_inspect(profile, root)
|
||||||
|
if path.startswith("/profiles/") and path.endswith("/validate"):
|
||||||
|
profile = path.removeprefix("/profiles/").removesuffix("/validate").strip("/")
|
||||||
|
payload = profile_validate(profile, root)
|
||||||
|
return (HTTPStatus.OK if payload["ok"] else HTTPStatus.BAD_REQUEST), payload
|
||||||
|
if path.startswith("/profiles/") and path.endswith("/graph"):
|
||||||
|
profile = path.removeprefix("/profiles/").removesuffix("/graph").strip("/")
|
||||||
|
graph_format = _first(query, "format") or "json"
|
||||||
|
return HTTPStatus.OK, profile_graph(profile, root, output_format=graph_format)
|
||||||
return HTTPStatus.NOT_FOUND, {
|
return HTTPStatus.NOT_FOUND, {
|
||||||
"ok": False,
|
"ok": False,
|
||||||
"error": {
|
"error": {
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ from .service import (
|
|||||||
list_models,
|
list_models,
|
||||||
list_standards,
|
list_standards,
|
||||||
list_views,
|
list_views,
|
||||||
|
profile_graph,
|
||||||
profile_inspect,
|
profile_inspect,
|
||||||
|
profile_validate,
|
||||||
read_view,
|
read_view,
|
||||||
validate_canon,
|
validate_canon,
|
||||||
write_validation_report,
|
write_validation_report,
|
||||||
@@ -81,6 +83,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
profile_inspect_cmd = profile_sub.add_parser("inspect", help="Inspect a profile")
|
profile_inspect_cmd = profile_sub.add_parser("inspect", help="Inspect a profile")
|
||||||
profile_inspect_cmd.add_argument("profile")
|
profile_inspect_cmd.add_argument("profile")
|
||||||
profile_inspect_cmd.set_defaults(handler=_profile_inspect)
|
profile_inspect_cmd.set_defaults(handler=_profile_inspect)
|
||||||
|
profile_validate_cmd = profile_sub.add_parser("validate", help="Validate a profile")
|
||||||
|
profile_validate_cmd.add_argument("profile")
|
||||||
|
profile_validate_cmd.set_defaults(handler=_profile_validate)
|
||||||
|
profile_graph_cmd = profile_sub.add_parser("graph", help="Export a profile graph")
|
||||||
|
profile_graph_cmd.add_argument("profile")
|
||||||
|
profile_graph_cmd.add_argument("--format", choices=["json", "mermaid"], default="json")
|
||||||
|
profile_graph_cmd.set_defaults(handler=_profile_graph)
|
||||||
|
|
||||||
api = sub.add_parser("api", help="Run the read-only local API")
|
api = sub.add_parser("api", help="Run the read-only local API")
|
||||||
api.add_argument("--host", default="127.0.0.1")
|
api.add_argument("--host", default="127.0.0.1")
|
||||||
@@ -168,6 +177,14 @@ def _profile_inspect(args: argparse.Namespace) -> dict[str, Any]:
|
|||||||
return profile_inspect(args.profile, _root(args))
|
return profile_inspect(args.profile, _root(args))
|
||||||
|
|
||||||
|
|
||||||
|
def _profile_validate(args: argparse.Namespace) -> dict[str, Any]:
|
||||||
|
return profile_validate(args.profile, _root(args))
|
||||||
|
|
||||||
|
|
||||||
|
def _profile_graph(args: argparse.Namespace) -> dict[str, Any]:
|
||||||
|
return profile_graph(args.profile, _root(args), output_format=args.format)
|
||||||
|
|
||||||
|
|
||||||
def _api(args: argparse.Namespace) -> dict[str, Any]:
|
def _api(args: argparse.Namespace) -> dict[str, Any]:
|
||||||
serve(host=args.host, port=args.port, root=_root(args))
|
serve(host=args.host, port=args.port, root=_root(args))
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
402
src/info_tech_canon/profiles.py
Normal file
402
src/info_tech_canon/profiles.py
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from .bench import export_mermaid, relationship_summary
|
||||||
|
|
||||||
|
|
||||||
|
REQUIRED_SMALL_SAAS_KINDS = {
|
||||||
|
"service",
|
||||||
|
"system",
|
||||||
|
"tenant",
|
||||||
|
"user",
|
||||||
|
"team",
|
||||||
|
"dataset",
|
||||||
|
"deployment",
|
||||||
|
"task",
|
||||||
|
"policy",
|
||||||
|
"control",
|
||||||
|
"evidence",
|
||||||
|
"incident",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def inspect_profile(context: Any, profile: str) -> dict[str, Any]:
|
||||||
|
definition = load_profile_definition(context, profile)
|
||||||
|
records = profile_artifact_records(context, profile)
|
||||||
|
return {
|
||||||
|
"ok": True,
|
||||||
|
"profile": definition,
|
||||||
|
"path": str(profile_path(context, profile)),
|
||||||
|
"artifact_count": len(records),
|
||||||
|
"artifacts": [artifact.to_dict() for artifact in records],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_profile(context: Any, profile: str) -> dict[str, Any]:
|
||||||
|
definition = load_profile_definition(context, profile)
|
||||||
|
records = profile_artifact_records(context, profile)
|
||||||
|
payloads = load_profile_artifacts(context, records)
|
||||||
|
errors: list[dict[str, Any]] = []
|
||||||
|
warnings: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
_check_profile_definition(profile, definition, records, errors)
|
||||||
|
_check_required_artifact_kinds(profile, payloads, errors)
|
||||||
|
_check_artifact_payloads(profile, payloads, errors)
|
||||||
|
_check_service_ownership(payloads, errors)
|
||||||
|
_check_tenant_namespace_separation(payloads, errors)
|
||||||
|
_check_user_management_and_access(payloads, errors)
|
||||||
|
_check_governance_evidence(payloads, errors)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ok": not errors,
|
||||||
|
"profile": profile,
|
||||||
|
"errors": errors,
|
||||||
|
"warnings": warnings,
|
||||||
|
"details": {
|
||||||
|
"artifact_count": len(records),
|
||||||
|
"payload_count": len(payloads),
|
||||||
|
"kinds": _kind_counts(payloads),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def profile_graph(
|
||||||
|
context: Any,
|
||||||
|
profile: str,
|
||||||
|
*,
|
||||||
|
output_format: str = "json",
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
records = profile_artifact_records(context, profile)
|
||||||
|
record_ids = {artifact.id for artifact in records}
|
||||||
|
include_ids = set(record_ids)
|
||||||
|
for artifact in records:
|
||||||
|
for relationship in artifact.relationships:
|
||||||
|
target = relationship.get("target")
|
||||||
|
if isinstance(target, str):
|
||||||
|
include_ids.add(target)
|
||||||
|
artifacts = [
|
||||||
|
artifact for artifact in context.infospace.artifacts if artifact.id in include_ids
|
||||||
|
]
|
||||||
|
summary = relationship_summary(artifacts)
|
||||||
|
if output_format == "mermaid":
|
||||||
|
return {"ok": True, "profile": profile, "format": "mermaid", "graph": export_mermaid(summary)}
|
||||||
|
if output_format != "json":
|
||||||
|
raise ValueError(f"Unsupported graph format: {output_format}")
|
||||||
|
return {
|
||||||
|
"ok": True,
|
||||||
|
"profile": profile,
|
||||||
|
"format": "json",
|
||||||
|
"graph": {
|
||||||
|
"node_count": summary.node_count,
|
||||||
|
"edge_count": summary.edge_count,
|
||||||
|
"nodes": summary.nodes,
|
||||||
|
"edges": [asdict(edge) for edge in summary.edges],
|
||||||
|
"relationship_types": summary.relationship_types,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def profile_path(context: Any, profile: str) -> Path:
|
||||||
|
return context.infospace_root / "profiles" / profile / "profile.yaml"
|
||||||
|
|
||||||
|
|
||||||
|
def load_profile_definition(context: Any, profile: str) -> dict[str, Any]:
|
||||||
|
path = profile_path(context, profile)
|
||||||
|
with path.open("r", encoding="utf-8") as handle:
|
||||||
|
data = yaml.safe_load(handle) or {}
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ValueError(f"Profile must be a YAML mapping: {profile}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def profile_artifact_records(context: Any, profile: str) -> list[Any]:
|
||||||
|
return [
|
||||||
|
artifact
|
||||||
|
for artifact in context.infospace.artifacts
|
||||||
|
if artifact.id == f"profile/{profile}"
|
||||||
|
or artifact.provenance.get("profile") == profile
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def load_profile_artifacts(context: Any, records: list[Any]) -> dict[str, dict[str, Any]]:
|
||||||
|
payloads: dict[str, dict[str, Any]] = {}
|
||||||
|
for artifact in records:
|
||||||
|
path = context.infospace_root / artifact.path
|
||||||
|
with path.open("r", encoding="utf-8") as handle:
|
||||||
|
data = yaml.safe_load(handle) or {}
|
||||||
|
if isinstance(data, dict):
|
||||||
|
payloads[artifact.id] = data
|
||||||
|
return payloads
|
||||||
|
|
||||||
|
|
||||||
|
def _check_profile_definition(
|
||||||
|
profile: str,
|
||||||
|
definition: dict[str, Any],
|
||||||
|
records: list[Any],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
for field in ("id", "title", "scope", "conformance_level", "required_standards"):
|
||||||
|
if not definition.get(field):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "missing_profile_field",
|
||||||
|
"profile": profile,
|
||||||
|
"field": field,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
declared = set(definition.get("artifact_ids") or [])
|
||||||
|
actual = {artifact.id for artifact in records}
|
||||||
|
missing = sorted(declared - actual)
|
||||||
|
for artifact_id in missing:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "missing_profile_artifact_record",
|
||||||
|
"profile": profile,
|
||||||
|
"artifact_id": artifact_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_required_artifact_kinds(
|
||||||
|
profile: str,
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
kinds = {payload.get("kind") for payload in payloads.values()}
|
||||||
|
for kind in sorted(REQUIRED_SMALL_SAAS_KINDS - kinds):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "missing_required_profile_kind",
|
||||||
|
"profile": profile,
|
||||||
|
"kind": kind,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_artifact_payloads(
|
||||||
|
profile: str,
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
ids = set(payloads)
|
||||||
|
for artifact_id, payload in payloads.items():
|
||||||
|
for field in ("id", "kind", "title", "profile"):
|
||||||
|
if not payload.get(field):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "missing_profile_artifact_field",
|
||||||
|
"profile": profile,
|
||||||
|
"artifact_id": artifact_id,
|
||||||
|
"field": field,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if payload.get("profile") not in {profile, None}:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "profile_artifact_profile_mismatch",
|
||||||
|
"profile": profile,
|
||||||
|
"artifact_id": artifact_id,
|
||||||
|
"value": payload.get("profile"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for relationship in payload.get("relationships") or []:
|
||||||
|
target = relationship.get("target")
|
||||||
|
if target and target not in ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "profile_relationship_target_not_in_payloads",
|
||||||
|
"profile": profile,
|
||||||
|
"artifact_id": artifact_id,
|
||||||
|
"target": target,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_service_ownership(
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
service = _one_kind(payloads, "service")
|
||||||
|
if not service:
|
||||||
|
return
|
||||||
|
owner_team = service.get("owner_team")
|
||||||
|
if not _exists_kind(payloads, owner_team, "team"):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "invalid_service_owner_team",
|
||||||
|
"artifact_id": service.get("id"),
|
||||||
|
"owner_team": owner_team,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
team = payloads.get(str(owner_team))
|
||||||
|
if team and not _exists_kind(payloads, team.get("owner_user"), "user"):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "invalid_team_owner_user",
|
||||||
|
"artifact_id": team.get("id"),
|
||||||
|
"owner_user": team.get("owner_user"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_tenant_namespace_separation(
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
tenants = [payload for payload in payloads.values() if payload.get("kind") == "tenant"]
|
||||||
|
namespaces = [tenant.get("namespace") for tenant in tenants]
|
||||||
|
if len(namespaces) != len(set(namespaces)):
|
||||||
|
errors.append({"code": "duplicate_tenant_namespace"})
|
||||||
|
deployment = _one_kind(payloads, "deployment")
|
||||||
|
if deployment:
|
||||||
|
if deployment.get("namespace_strategy") != "namespace-per-tenant":
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "invalid_namespace_strategy",
|
||||||
|
"artifact_id": deployment.get("id"),
|
||||||
|
"namespace_strategy": deployment.get("namespace_strategy"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
tenant_namespaces = deployment.get("tenant_namespaces") or {}
|
||||||
|
for tenant in tenants:
|
||||||
|
if tenant.get("id") not in tenant_namespaces:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "tenant_missing_deployment_namespace",
|
||||||
|
"tenant_id": tenant.get("id"),
|
||||||
|
"deployment_id": deployment.get("id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
dataset = _one_kind(payloads, "dataset")
|
||||||
|
if dataset:
|
||||||
|
if dataset.get("tenant_scope") != "per-tenant":
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "dataset_not_per_tenant",
|
||||||
|
"artifact_id": dataset.get("id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
tenant_ids = set(dataset.get("tenant_ids") or [])
|
||||||
|
for tenant in tenants:
|
||||||
|
if tenant.get("id") not in tenant_ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "tenant_missing_dataset_partition",
|
||||||
|
"tenant_id": tenant.get("id"),
|
||||||
|
"dataset_id": dataset.get("id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_user_management_and_access(
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
evidence_ids = _ids_by_kind(payloads, "evidence")
|
||||||
|
policy_ids = _ids_by_kind(payloads, "policy")
|
||||||
|
tenant_ids = _ids_by_kind(payloads, "tenant")
|
||||||
|
for user in [payload for payload in payloads.values() if payload.get("kind") == "user"]:
|
||||||
|
if not user.get("teams"):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "user_missing_team_membership",
|
||||||
|
"artifact_id": user.get("id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
grants = user.get("access_grants") or []
|
||||||
|
if not grants:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "user_missing_access_grant",
|
||||||
|
"artifact_id": user.get("id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for grant in grants:
|
||||||
|
if grant.get("tenant_id") not in tenant_ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "access_grant_missing_tenant",
|
||||||
|
"artifact_id": user.get("id"),
|
||||||
|
"tenant_id": grant.get("tenant_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if grant.get("policy_id") not in policy_ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "access_grant_missing_policy",
|
||||||
|
"artifact_id": user.get("id"),
|
||||||
|
"policy_id": grant.get("policy_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if grant.get("evidence_id") not in evidence_ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "access_grant_missing_evidence",
|
||||||
|
"artifact_id": user.get("id"),
|
||||||
|
"evidence_id": grant.get("evidence_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_governance_evidence(
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
errors: list[dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
|
evidence_ids = _ids_by_kind(payloads, "evidence")
|
||||||
|
for kind in ("policy", "control", "incident", "service", "dataset", "deployment", "task"):
|
||||||
|
for payload in [item for item in payloads.values() if item.get("kind") == kind]:
|
||||||
|
ids = set(payload.get("evidence_ids") or [])
|
||||||
|
if not ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "artifact_missing_evidence",
|
||||||
|
"artifact_id": payload.get("id"),
|
||||||
|
"kind": kind,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for evidence_id in ids:
|
||||||
|
if evidence_id not in evidence_ids:
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"code": "artifact_missing_evidence_target",
|
||||||
|
"artifact_id": payload.get("id"),
|
||||||
|
"evidence_id": evidence_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _kind_counts(payloads: dict[str, dict[str, Any]]) -> dict[str, int]:
|
||||||
|
counts: dict[str, int] = {}
|
||||||
|
for payload in payloads.values():
|
||||||
|
kind = str(payload.get("kind") or "unknown")
|
||||||
|
counts[kind] = counts.get(kind, 0) + 1
|
||||||
|
return dict(sorted(counts.items()))
|
||||||
|
|
||||||
|
|
||||||
|
def _one_kind(payloads: dict[str, dict[str, Any]], kind: str) -> dict[str, Any] | None:
|
||||||
|
for payload in payloads.values():
|
||||||
|
if payload.get("kind") == kind:
|
||||||
|
return payload
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _exists_kind(
|
||||||
|
payloads: dict[str, dict[str, Any]],
|
||||||
|
artifact_id: Any,
|
||||||
|
kind: str,
|
||||||
|
) -> bool:
|
||||||
|
payload = payloads.get(str(artifact_id))
|
||||||
|
return bool(payload and payload.get("kind") == kind)
|
||||||
|
|
||||||
|
|
||||||
|
def _ids_by_kind(payloads: dict[str, dict[str, Any]], kind: str) -> set[str]:
|
||||||
|
return {
|
||||||
|
str(payload["id"])
|
||||||
|
for payload in payloads.values()
|
||||||
|
if payload.get("kind") == kind and payload.get("id")
|
||||||
|
}
|
||||||
@@ -6,9 +6,8 @@ from pathlib import Path
|
|||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from . import generation
|
from . import generation
|
||||||
|
from . import profiles
|
||||||
from .bench import (
|
from .bench import (
|
||||||
Infospace,
|
Infospace,
|
||||||
KnowledgeArtifact,
|
KnowledgeArtifact,
|
||||||
@@ -230,15 +229,53 @@ def profile_inspect(
|
|||||||
f"Profile not found: {profile}",
|
f"Profile not found: {profile}",
|
||||||
{"profile": profile, "path": str(profile_path)},
|
{"profile": profile, "path": str(profile_path)},
|
||||||
)
|
)
|
||||||
with profile_path.open("r", encoding="utf-8") as handle:
|
try:
|
||||||
data = yaml.safe_load(handle) or {}
|
return profiles.inspect_profile(context, profile)
|
||||||
if not isinstance(data, dict):
|
except ValueError as exc:
|
||||||
raise CanonServiceError(
|
raise CanonServiceError(
|
||||||
"invalid_profile",
|
"invalid_profile",
|
||||||
f"Profile must be a YAML mapping: {profile}",
|
f"Profile must be a YAML mapping: {profile}",
|
||||||
{"profile": profile, "path": str(profile_path)},
|
{"profile": profile, "path": str(profile_path)},
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def profile_validate(
|
||||||
|
profile: str,
|
||||||
|
root: Path | str | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
context = load_context(root)
|
||||||
|
profile_path = context.infospace_root / "profiles" / profile / "profile.yaml"
|
||||||
|
if not profile_path.is_file():
|
||||||
|
raise CanonServiceError(
|
||||||
|
"missing_profile",
|
||||||
|
f"Profile not found: {profile}",
|
||||||
|
{"profile": profile, "path": str(profile_path)},
|
||||||
)
|
)
|
||||||
return {"ok": True, "profile": data, "path": str(profile_path)}
|
return profiles.validate_profile(context, profile)
|
||||||
|
|
||||||
|
|
||||||
|
def profile_graph(
|
||||||
|
profile: str,
|
||||||
|
root: Path | str | None = None,
|
||||||
|
*,
|
||||||
|
output_format: str = "json",
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
context = load_context(root)
|
||||||
|
profile_path = context.infospace_root / "profiles" / profile / "profile.yaml"
|
||||||
|
if not profile_path.is_file():
|
||||||
|
raise CanonServiceError(
|
||||||
|
"missing_profile",
|
||||||
|
f"Profile not found: {profile}",
|
||||||
|
{"profile": profile, "path": str(profile_path)},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
return profiles.profile_graph(context, profile, output_format=output_format)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise CanonServiceError(
|
||||||
|
"unsupported_graph_format",
|
||||||
|
str(exc),
|
||||||
|
{"supported": ["json", "mermaid"]},
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
def generate_indexes(root: Path | str | None = None) -> dict[str, Any]:
|
def generate_indexes(root: Path | str | None = None) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -39,3 +39,11 @@ def test_api_route_reads_generated_view(tmp_path) -> None:
|
|||||||
assert payload["ok"] is True
|
assert payload["ok"] is True
|
||||||
assert payload["generated"] is True
|
assert payload["generated"] is True
|
||||||
assert "# By Standard" in payload["content"]
|
assert "# By Standard" in payload["content"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_route_validates_small_saas_profile() -> None:
|
||||||
|
status, payload = _route("/profiles/small-saas/validate", {}, None)
|
||||||
|
|
||||||
|
assert status == HTTPStatus.OK
|
||||||
|
assert payload["ok"] is True
|
||||||
|
assert payload["details"]["artifact_count"] == 14
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ def test_cli_inspect_emits_json(capsys) -> None:
|
|||||||
assert exit_code == 0
|
assert exit_code == 0
|
||||||
payload = json.loads(capsys.readouterr().out)
|
payload = json.loads(capsys.readouterr().out)
|
||||||
assert payload["ok"] is True
|
assert payload["ok"] is True
|
||||||
assert payload["infospace"]["artifact_count"] == 15
|
assert payload["infospace"]["artifact_count"] == 29
|
||||||
|
|
||||||
|
|
||||||
def test_cli_missing_profile_uses_structured_error(capsys) -> None:
|
def test_cli_missing_profile_uses_structured_error(capsys) -> None:
|
||||||
exit_code = main(["profile", "inspect", "small-saas"])
|
exit_code = main(["profile", "inspect", "missing-profile"])
|
||||||
|
|
||||||
assert exit_code == 2
|
assert exit_code == 2
|
||||||
payload = json.loads(capsys.readouterr().out)
|
payload = json.loads(capsys.readouterr().out)
|
||||||
@@ -23,6 +23,15 @@ def test_cli_missing_profile_uses_structured_error(capsys) -> None:
|
|||||||
assert payload["error"]["code"] == "missing_profile"
|
assert payload["error"]["code"] == "missing_profile"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_small_saas_profile_validate(capsys) -> None:
|
||||||
|
exit_code = main(["profile", "validate", "small-saas"])
|
||||||
|
|
||||||
|
assert exit_code == 0
|
||||||
|
payload = json.loads(capsys.readouterr().out)
|
||||||
|
assert payload["ok"] is True
|
||||||
|
assert payload["details"]["kinds"]["service"] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_cli_index_generates_views(capsys, tmp_path) -> None:
|
def test_cli_index_generates_views(capsys, tmp_path) -> None:
|
||||||
root = tmp_path / "infospace"
|
root = tmp_path / "infospace"
|
||||||
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)
|
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from info_tech_canon.service import (
|
|||||||
inspect_canon,
|
inspect_canon,
|
||||||
list_models,
|
list_models,
|
||||||
list_standards,
|
list_standards,
|
||||||
|
profile_graph,
|
||||||
|
profile_validate,
|
||||||
validate_canon,
|
validate_canon,
|
||||||
)
|
)
|
||||||
from info_tech_canon.service import DEFAULT_INFOSPACE_ROOT
|
from info_tech_canon.service import DEFAULT_INFOSPACE_ROOT
|
||||||
@@ -17,10 +19,12 @@ def test_inspect_canon_counts_artifact_kinds() -> None:
|
|||||||
|
|
||||||
assert payload["ok"] is True
|
assert payload["ok"] is True
|
||||||
assert payload["infospace"]["slug"] == "canon"
|
assert payload["infospace"]["slug"] == "canon"
|
||||||
assert payload["infospace"]["artifact_count"] == 15
|
assert payload["infospace"]["artifact_count"] == 29
|
||||||
assert payload["infospace"]["kinds"] == {
|
assert payload["infospace"]["kinds"] == {
|
||||||
"kernel": 2,
|
"kernel": 2,
|
||||||
"model": 11,
|
"model": 11,
|
||||||
|
"profile": 1,
|
||||||
|
"profile-artifact": 13,
|
||||||
"standard": 2,
|
"standard": 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,17 +40,33 @@ def test_validate_canon_passes_scaffold() -> None:
|
|||||||
assert payload["ok"] is True
|
assert payload["ok"] is True
|
||||||
assert payload["errors"] == []
|
assert payload["errors"] == []
|
||||||
assert "warnings" in payload
|
assert "warnings" in payload
|
||||||
assert payload["details"]["artifact_count"] == 15
|
assert payload["details"]["artifact_count"] == 29
|
||||||
|
|
||||||
|
|
||||||
def test_graph_exports_relationship_summary() -> None:
|
def test_graph_exports_relationship_summary() -> None:
|
||||||
payload = artifact_graph()
|
payload = artifact_graph()
|
||||||
|
|
||||||
assert payload["ok"] is True
|
assert payload["ok"] is True
|
||||||
assert payload["graph"]["node_count"] == 15
|
assert payload["graph"]["node_count"] == 29
|
||||||
assert payload["graph"]["edge_count"] > 15
|
assert payload["graph"]["edge_count"] > 15
|
||||||
|
|
||||||
|
|
||||||
|
def test_small_saas_profile_validates() -> None:
|
||||||
|
payload = profile_validate("small-saas")
|
||||||
|
|
||||||
|
assert payload["ok"] is True
|
||||||
|
assert payload["errors"] == []
|
||||||
|
assert payload["details"]["kinds"]["tenant"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_small_saas_profile_graph_exports_slice() -> None:
|
||||||
|
payload = profile_graph("small-saas")
|
||||||
|
|
||||||
|
assert payload["ok"] is True
|
||||||
|
assert payload["profile"] == "small-saas"
|
||||||
|
assert "small-saas/service/billing-portal" in payload["graph"]["nodes"]
|
||||||
|
|
||||||
|
|
||||||
def test_generators_write_expected_assets(tmp_path) -> None:
|
def test_generators_write_expected_assets(tmp_path) -> None:
|
||||||
root = tmp_path / "infospace"
|
root = tmp_path / "infospace"
|
||||||
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)
|
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type: workplan
|
|||||||
title: "Small SaaS Profile Proof"
|
title: "Small SaaS Profile Proof"
|
||||||
domain: canon
|
domain: canon
|
||||||
repo: info-tech-canon
|
repo: info-tech-canon
|
||||||
status: proposed
|
status: finished
|
||||||
priority: high
|
priority: high
|
||||||
created: "2026-05-23"
|
created: "2026-05-23"
|
||||||
updated: "2026-05-23"
|
updated: "2026-05-23"
|
||||||
@@ -35,7 +35,7 @@ governance, observability, and work tracking.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ITC-WP-0004-T01
|
id: ITC-WP-0004-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "21801d10-2ec9-45b9-bb7a-632f251075d5"
|
state_hub_task_id: "21801d10-2ec9-45b9-bb7a-632f251075d5"
|
||||||
```
|
```
|
||||||
@@ -48,7 +48,7 @@ state_hub_task_id: "21801d10-2ec9-45b9-bb7a-632f251075d5"
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ITC-WP-0004-T02
|
id: ITC-WP-0004-T02
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "92c860ca-174d-4d31-b11c-66750a2d3588"
|
state_hub_task_id: "92c860ca-174d-4d31-b11c-66750a2d3588"
|
||||||
```
|
```
|
||||||
@@ -62,7 +62,7 @@ state_hub_task_id: "92c860ca-174d-4d31-b11c-66750a2d3588"
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ITC-WP-0004-T03
|
id: ITC-WP-0004-T03
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "b342257f-beef-4ec6-b116-b9f8798df10c"
|
state_hub_task_id: "b342257f-beef-4ec6-b116-b9f8798df10c"
|
||||||
```
|
```
|
||||||
@@ -77,7 +77,7 @@ state_hub_task_id: "b342257f-beef-4ec6-b116-b9f8798df10c"
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ITC-WP-0004-T04
|
id: ITC-WP-0004-T04
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "58a85249-be34-497d-a19c-96b6b343b076"
|
state_hub_task_id: "58a85249-be34-497d-a19c-96b6b343b076"
|
||||||
```
|
```
|
||||||
@@ -93,3 +93,16 @@ state_hub_task_id: "58a85249-be34-497d-a19c-96b6b343b076"
|
|||||||
- The proof produces useful graph and JSON outputs.
|
- The proof produces useful graph and JSON outputs.
|
||||||
- The result is suitable as the baseline for user-engine and railiance-fabric
|
- The result is suitable as the baseline for user-engine and railiance-fabric
|
||||||
evaluation.
|
evaluation.
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- Added `infospace/profiles/small-saas/profile.yaml` with scope,
|
||||||
|
assumptions, required standards, concept obligations, validation rules, and
|
||||||
|
demo commands.
|
||||||
|
- Added connected profile artifacts for service, system, tenants, user, team,
|
||||||
|
dataset, deployment, task, policy, control, evidence, and incident.
|
||||||
|
- Registered the profile and examples in `infospace/artifacts/index.yaml` so
|
||||||
|
the graph and global validation include the proof.
|
||||||
|
- Added `profile validate` and `profile graph` service, CLI, and API support.
|
||||||
|
- Added a profile report and example command fixture suitable for downstream
|
||||||
|
user-engine and railiance-fabric evaluation work.
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ workplans:
|
|||||||
|
|
||||||
- id: ITC-WP-0004
|
- id: ITC-WP-0004
|
||||||
title: Small SaaS Profile Proof
|
title: Small SaaS Profile Proof
|
||||||
status: proposed
|
status: finished
|
||||||
priority: high
|
priority: high
|
||||||
path: workplans/ITC-WP-0004-small-saas-profile-proof.md
|
path: workplans/ITC-WP-0004-small-saas-profile-proof.md
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
Reference in New Issue
Block a user