Add small SaaS profile proof

This commit is contained in:
2026-05-23 04:26:28 +02:00
parent 79e4d10b68
commit 6351ebc627
40 changed files with 1696 additions and 54 deletions

View File

@@ -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 index
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
```
@@ -43,6 +46,8 @@ After package installation, the same commands are available through the
- `GET /views`
- `GET /views/{name}`
- `GET /profiles/{profile}/inspect`
- `GET /profiles/{profile}/validate`
- `GET /profiles/{profile}/graph`
## Maintenance
@@ -52,3 +57,10 @@ make index
make tree
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.

View File

@@ -5,7 +5,7 @@
This brief summarizes the current canon service surface for agents.
- Infospace slug: `canon`
- Artifact count: 15
- Artifact count: 29
- Primary confidence command: `make validate`
- Refresh generated indexes and views with: `make index`

View File

@@ -224,3 +224,252 @@ artifacts:
target: model/task
- type: imports
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

View 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
```

View 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

View File

@@ -1,5 +1,5 @@
root: infospace
file_count: 50
file_count: 67
files:
- path: README.md
directory: .
@@ -19,6 +19,12 @@ files:
- path: examples/README.md
directory: examples
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
directory: indexes
name: README.md
@@ -82,9 +88,54 @@ files:
- path: profiles/README.md
directory: profiles
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
directory: reports
name: scaffold-placement.md
- path: reports/small-saas-profile-proof.md
directory: reports
name: small-saas-profile-proof.md
- path: schemas/README.md
directory: schemas
name: README.md

View File

@@ -1,4 +1,4 @@
concept_count: 30
concept_count: 44
concepts:
- concept: InfoTechCanon Core
owner: kernel/itc-core
@@ -52,6 +52,62 @@ concepts:
owner: model/task
path: models/task/InfoTechCanonTaskModel.md
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
owner: standard/caring
path: standards/caring/InfoTechCanonCaringAccessGovernanceStandard.md

View File

@@ -12,6 +12,20 @@ artifacts:
- 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
rows:
@@ -105,6 +119,170 @@ rows:
targets:
kernel/itc-core:
- 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
targets:
kernel/itc-core:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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

View 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

View 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

View 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.

View File

@@ -1,14 +1,14 @@
{
"details": {
"artifact_count": 15,
"relationship_count": 45
"artifact_count": 29,
"relationship_count": 113
},
"errors": [],
"metrics": {
"coherence_components": 1.0,
"consistency_cycles": 0.0,
"coverage_ratio": 1.0,
"granularity_entropy": 1.103307408607834,
"granularity_entropy": 1.7490339557881076,
"redundancy_ratio": 0.0
},
"ok": true,
@@ -17,10 +17,6 @@
"code": "missing_optional_concepts_dir",
"path": "infospace/concepts"
},
{
"code": "empty_optional_collection",
"path": "infospace/profiles"
},
{
"code": "empty_optional_collection",
"path": "infospace/patterns"
@@ -32,10 +28,6 @@
{
"code": "empty_optional_collection",
"path": "infospace/assimilation"
},
{
"code": "empty_optional_collection",
"path": "infospace/examples"
}
]
}

View File

@@ -2,7 +2,7 @@
# By Concept
Concept count: **30**
Concept count: **44**
| Concept | Owner | Source |
| --- | --- | --- |
@@ -19,6 +19,20 @@ Concept count: **30**
| InfoTechCanon Organization Model | `model/organization` | `artifact_title` |
| InfoTechCanon Security Model | `model/security` | `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` |
| CARINGAccessDescriptor | `standard/caring` | `frontmatter.owned_concepts` |
| CARINGCanonicalRole | `standard/caring` | `frontmatter.owned_concepts` |

View File

@@ -16,6 +16,7 @@
- `model/organization` via `conforms_to`
- `model/security` via `conforms_to`
- `model/task` via `conforms_to`
- `profile/small-saas` via `conforms_to`
- `standard/caring` via `conforms_to`
- `standard/tagging` via `conforms_to`
@@ -23,16 +24,22 @@
- `kernel/itc-kernel-map` via `maps`
- `model/security` via `uses`
- `profile/small-saas` via `requires`
- `small-saas/user/ada-admin` via `uses`
- `standard/caring` via `imports`
## `model/data`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `small-saas/dataset/subscription-ledger` via `uses`
- `standard/caring` via `imports`
## `model/devsecops`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `small-saas/deployment/production` via `uses`
- `standard/caring` via `imports`
## `model/governance`
@@ -40,6 +47,8 @@
- `kernel/itc-kernel-map` via `maps`
- `model/access-control` via `uses`
- `model/data` via `uses`
- `profile/small-saas` via `requires`
- `small-saas/policy/tenant-isolation` via `uses`
- `standard/caring` via `imports`
## `model/information-space`
@@ -49,21 +58,33 @@
## `model/landscape`
- `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`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `small-saas/deployment/production` via `uses`
- `standard/caring` via `imports`
## `model/observability`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `small-saas/evidence/access-review-2026-05` via `uses`
- `standard/caring` via `imports`
## `model/organization`
- `kernel/itc-kernel-map` via `maps`
- `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`
## `model/security`
@@ -71,20 +92,97 @@
- `kernel/itc-kernel-map` via `maps`
- `model/devsecops` 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`
## `model/task`
- `kernel/itc-kernel-map` via `maps`
- `model/observability` via `uses`
- `profile/small-saas` via `requires`
- `small-saas/task/onboard-tenant` via `uses`
- `standard/caring` 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`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `small-saas/control/namespace-per-tenant` via `uses`
## `standard/tagging`
- `kernel/itc-kernel-map` via `maps`
- `profile/small-saas` via `requires`
- `standard/caring` via `imports`

View File

@@ -2,4 +2,6 @@
# By Profile
No profiles have been registered yet.
## small-saas
- Path: `profiles/small-saas/profile.yaml`

View File

@@ -2,20 +2,34 @@
# 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` |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `kernel/itc-core` | | | | | | | | | | | | | | | |
| `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/data` | `conforms_to` | | | | | `uses` | | | | | | | | | |
| `model/devsecops` | `conforms_to` | | | | | | | | | | | `uses` | | | |
| `model/governance` | `conforms_to` | | | | | | | | | | | | | | |
| `model/information-space` | `conforms_to` | | | | | | | | | | | | | | |
| `model/landscape` | `conforms_to` | | | | | | | | | | | | | | |
| `model/network` | `conforms_to` | | | | | | | | | | | `uses` | | | |
| `model/observability` | `conforms_to` | | | | | | | | | | | | `uses` | | |
| `model/organization` | `conforms_to` | | | | | | | | | | | | | | |
| `model/security` | `conforms_to` | | `uses` | | | | | | | | | | | | |
| `model/task` | `conforms_to` | | | | | | | | | | | | | | |
| `standard/caring` | `conforms_to` | | `imports` | `imports` | `imports` | `imports` | | | `imports` | `imports` | `imports` | `imports` | `imports` | | `imports` |
| `standard/tagging` | `conforms_to` | | | | | | | | | | | | `imports` | | |
| 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-kernel-map` | `maps` | | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | `maps` | | | | | | | | | | | | | | | `maps` | `maps` |
| `model/access-control` | `conforms_to` | | | | | `uses` | | | | | `uses` | | | | | | | | | | | | | | | | | | |
| `model/data` | `conforms_to` | | | | | `uses` | | | | | | | | | | | | | | | | | | | | | | | |
| `model/devsecops` | `conforms_to` | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | | |
| `model/governance` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `model/information-space` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `model/landscape` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `model/network` | `conforms_to` | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | | |
| `model/observability` | `conforms_to` | | | | | | | | | | | | `uses` | | | | | | | | | | | | | | | | |
| `model/organization` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `model/security` | `conforms_to` | | `uses` | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `model/task` | `conforms_to` | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| `profile/small-saas` | `conforms_to` | | `requires` | `requires` | `requires` | `requires` | | `requires` | `requires` | `requires` | `requires` | `requires` | `requires` | | | | | | | | | | | | | | | `requires` | `requires` |
| `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` | | | | | | | | | | | | | | | | |

View File

@@ -3,17 +3,37 @@
# Kernel Overview
- Infospace: `canon`
- Artifacts: 15
- Artifacts: 29
## Artifact Kinds
- `kernel`: 2
- `model`: 11
- `profile`: 1
- `profile-artifact`: 13
- `standard`: 2
## 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
- `instantiates`: 13
- `isolated_by`: 2
- `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

View File

@@ -2,7 +2,7 @@
# Repository Tree
File count: **50**
File count: **67**
- `README.md`
- `agent/README.md`
@@ -10,6 +10,8 @@ File count: **50**
- `artifacts/index.yaml`
- `assimilation/README.md`
- `examples/README.md`
- `examples/small-saas/README.md`
- `examples/small-saas/demo-commands.yaml`
- `indexes/README.md`
- `indexes/artifact-tree.yaml`
- `indexes/concept-ownership.yaml`
@@ -31,7 +33,22 @@ File count: **50**
- `models/task/InfoTechCanonTaskModel.md`
- `patterns/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/small-saas-profile-proof.md`
- `schemas/README.md`
- `schemas/agent-brief.schema.yaml`
- `schemas/assimilation.schema.yaml`

View File

@@ -11,7 +11,9 @@ from .service import (
list_models,
list_standards,
list_views,
profile_graph,
profile_inspect,
profile_validate,
read_view,
validate_canon,
write_validation_report,
@@ -28,7 +30,9 @@ __all__ = [
"list_models",
"list_standards",
"list_views",
"profile_graph",
"profile_inspect",
"profile_validate",
"read_view",
"validate_canon",
"write_validation_report",

View File

@@ -15,7 +15,9 @@ from .service import (
list_models,
list_standards,
list_views,
profile_graph,
profile_inspect,
profile_validate,
read_view,
validate_canon,
)
@@ -96,6 +98,14 @@ def _route(
if path.startswith("/profiles/") and path.endswith("/inspect"):
profile = path.removeprefix("/profiles/").removesuffix("/inspect").strip("/")
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, {
"ok": False,
"error": {

View File

@@ -19,7 +19,9 @@ from .service import (
list_models,
list_standards,
list_views,
profile_graph,
profile_inspect,
profile_validate,
read_view,
validate_canon,
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.add_argument("profile")
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.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))
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]:
serve(host=args.host, port=args.port, root=_root(args))
return {}

View 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")
}

View File

@@ -6,9 +6,8 @@ from pathlib import Path
import json
from typing import Any
import yaml
from . import generation
from . import profiles
from .bench import (
Infospace,
KnowledgeArtifact,
@@ -230,15 +229,53 @@ def profile_inspect(
f"Profile not found: {profile}",
{"profile": profile, "path": str(profile_path)},
)
with profile_path.open("r", encoding="utf-8") as handle:
data = yaml.safe_load(handle) or {}
if not isinstance(data, dict):
try:
return profiles.inspect_profile(context, profile)
except ValueError as exc:
raise CanonServiceError(
"invalid_profile",
f"Profile must be a YAML mapping: {profile}",
{"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]:

View File

@@ -39,3 +39,11 @@ def test_api_route_reads_generated_view(tmp_path) -> None:
assert payload["ok"] is True
assert payload["generated"] is True
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

View File

@@ -11,11 +11,11 @@ def test_cli_inspect_emits_json(capsys) -> None:
assert exit_code == 0
payload = json.loads(capsys.readouterr().out)
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:
exit_code = main(["profile", "inspect", "small-saas"])
exit_code = main(["profile", "inspect", "missing-profile"])
assert exit_code == 2
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"
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:
root = tmp_path / "infospace"
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)

View File

@@ -6,6 +6,8 @@ from info_tech_canon.service import (
inspect_canon,
list_models,
list_standards,
profile_graph,
profile_validate,
validate_canon,
)
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["infospace"]["slug"] == "canon"
assert payload["infospace"]["artifact_count"] == 15
assert payload["infospace"]["artifact_count"] == 29
assert payload["infospace"]["kinds"] == {
"kernel": 2,
"model": 11,
"profile": 1,
"profile-artifact": 13,
"standard": 2,
}
@@ -36,17 +40,33 @@ def test_validate_canon_passes_scaffold() -> None:
assert payload["ok"] is True
assert payload["errors"] == []
assert "warnings" in payload
assert payload["details"]["artifact_count"] == 15
assert payload["details"]["artifact_count"] == 29
def test_graph_exports_relationship_summary() -> None:
payload = artifact_graph()
assert payload["ok"] is True
assert payload["graph"]["node_count"] == 15
assert payload["graph"]["node_count"] == 29
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:
root = tmp_path / "infospace"
shutil.copytree(DEFAULT_INFOSPACE_ROOT, root)

View File

@@ -4,7 +4,7 @@ type: workplan
title: "Small SaaS Profile Proof"
domain: canon
repo: info-tech-canon
status: proposed
status: finished
priority: high
created: "2026-05-23"
updated: "2026-05-23"
@@ -35,7 +35,7 @@ governance, observability, and work tracking.
```task
id: ITC-WP-0004-T01
status: todo
status: done
priority: high
state_hub_task_id: "21801d10-2ec9-45b9-bb7a-632f251075d5"
```
@@ -48,7 +48,7 @@ state_hub_task_id: "21801d10-2ec9-45b9-bb7a-632f251075d5"
```task
id: ITC-WP-0004-T02
status: todo
status: done
priority: high
state_hub_task_id: "92c860ca-174d-4d31-b11c-66750a2d3588"
```
@@ -62,7 +62,7 @@ state_hub_task_id: "92c860ca-174d-4d31-b11c-66750a2d3588"
```task
id: ITC-WP-0004-T03
status: todo
status: done
priority: high
state_hub_task_id: "b342257f-beef-4ec6-b116-b9f8798df10c"
```
@@ -77,7 +77,7 @@ state_hub_task_id: "b342257f-beef-4ec6-b116-b9f8798df10c"
```task
id: ITC-WP-0004-T04
status: todo
status: done
priority: medium
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 result is suitable as the baseline for user-engine and railiance-fabric
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.

View File

@@ -56,7 +56,7 @@ workplans:
- id: ITC-WP-0004
title: Small SaaS Profile Proof
status: proposed
status: finished
priority: high
path: workplans/ITC-WP-0004-small-saas-profile-proof.md
depends_on: