7-Tier Data Classification: Security Labeling Framework from Public to Top Secret
coomia-dip implements a 7-tier data classification system (A1-Public, A2-Internal Public, B1-Internal Sensitive, B2-Confidential, C1-Highly Confidential, C2-Restricted, D-Top Secret) that provides a unified metadata foundation for access control, dynamic masking, and compliance auditing. Classification labels are embedded in the Ontology Schema and propagated automatically through Protobuf in gRPC call chains. This article covers the complete framework from classification design philosophy, technical implementation, and policy engine integration to operational management.
“Series: S6 Platform Engineering · Article 7 | Level: Advanced | Reading Time: 18 min
7-Tier Data Classification: Security Labeling Framework from Public to Top Secret
#TL;DR
coomia-dip implements a 7-tier data classification system (A1-Public, A2-Internal Public, B1-Internal Sensitive, B2-Confidential, C1-Highly Confidential, C2-Restricted, D-Top Secret) that provides a unified metadata foundation for access control, dynamic masking, and compliance auditing. Classification labels are embedded in the Ontology Schema and propagated automatically through Protobuf in gRPC call chains. This article covers the complete framework from classification design philosophy, technical implementation, and policy engine integration to operational management.
#1. The Need for Data Classification
#1.1 Chaos Without Classification
Without a unified data classification system, security policies are difficult to enforce consistently:
- Insufficient permission granularity: Cannot distinguish access levels between "general customer info" and "customer SSN"
- Ambiguous masking policies: Developers rely on intuition to determine which fields need masking, leading to inconsistent protection
- Compliance risk: Cannot demonstrate that appropriate protective measures are applied to data of different sensitivity levels
- Data governance blind spots: Metadata catalog lacks classification dimensions, preventing sensitivity-based search and management
#1.2 Design Motivation for 7 Tiers
The coomia-dip 7-tier classification system considers the following:
| Tier | Code | Name | Description | Typical Data |
|---|---|---|---|---|
| 1 | A1 | Public | Data that can be publicly released | Product catalogs, announcements |
| 2 | A2 | Internal Public | Freely accessible within the organization | Internal wikis, process docs |
| 3 | B1 | Internal Sensitive | Need-to-know access | Business reports, project data |
| 4 | B2 | Confidential | Restricted scope access | Financial data, contracts |
| 5 | C1 | Highly Confidential | Strict access control | Customer PII, medical records |
| 6 | C2 | Restricted | Minimum necessary access | Key material, security configs |
| 7 | D | Top Secret | Named custodians only | Defense/regulatory core data |
#1.3 Comparison with Palantir Foundry
| Capability | Palantir Foundry | coomia-dip |
|---|---|---|
| Classification tiers | Multi-layer Marking | 7-tier (A1-D) |
| Auto-labeling | Partial support | Rule engine auto-labeling |
| Propagation mechanism | Built-in but opaque | Explicit Protobuf propagation |
| Inheritance rules | High-water mark | Configurable inheritance policy |
| Masking integration | Built-in | ABAC-driven mapping |
#2. Classification Model Design
#2.1 Classification Level Enumeration
from enum import IntEnum
class ClassificationLevel(IntEnum):
"""Data Classification Level - 7-Tier System"""
PUBLIC = 1 # A1 - Public
INTERNAL_PUBLIC = 2 # A2 - Internal Public
INTERNAL_SENSITIVE = 3 # B1 - Internal Sensitive
CONFIDENTIAL = 4 # B2 - Confidential
HIGHLY_CONFIDENTIAL = 5 # C1 - Highly Confidential
RESTRICTED = 6 # C2 - Restricted
TOP_SECRET = 7 # D - Top Secret
@property
def code(self) -> str:
return {
1: "A1", 2: "A2", 3: "B1", 4: "B2",
5: "C1", 6: "C2", 7: "D",
}[self.value]
@property
def requires_encryption_at_rest(self) -> bool:
return self.value >= 4 # B2 and above
@property
def requires_audit_on_access(self) -> bool:
return self.value >= 3 # B1 and above
@property
def max_retention_days(self) -> int | None:
return {
5: 365 * 3, # C1: 3 years
6: 365 * 5, # C2: 5 years
7: 365 * 10, # D: 10 years
}.get(self.value)
#2.2 Classification Label Model
class ClassificationLabel(BaseModel):
"""Data Classification Label"""
level: ClassificationLevel = Field(description="Classification level")
category: DataCategory = Field(description="Data category")
sub_category: str | None = Field(default=None, description="Sub-category")
# Label metadata
labeled_by: str = Field(description="Labeler (person/system)")
labeled_at: datetime = Field(description="Labeling timestamp")
review_status: ReviewStatus = Field(default=ReviewStatus.PENDING)
reviewed_by: str | None = Field(default=None)
reviewed_at: datetime | None = Field(default=None)
# Label source
source: LabelSource = Field(description="Label source")
confidence: float = Field(default=1.0, ge=0.0, le=1.0, description="Confidence score")
# Compliance tags
regulatory_tags: list[str] = Field(
default_factory=list,
description="Associated regulatory tags (e.g., GDPR, HIPAA)",
)
class DataCategory(str, Enum):
PII = "pii" # Personally Identifiable Information
PHI = "phi" # Protected Health Information
PCI = "pci" # Payment Card Industry
FINANCIAL = "financial"
BUSINESS = "business"
TECHNICAL = "technical"
OPERATIONAL = "operational"
class LabelSource(str, Enum):
MANUAL = "manual"
RULE_ENGINE = "rule_engine"
ML_CLASSIFIER = "ml_classifier"
INHERITED = "inherited"
SCHEMA_DEFAULT = "schema_default"
#2.3 Schema Integration
Classification labels are embedded in Ontology Schema property definitions:
class PropertyDefinition(BaseModel):
"""Ontology property definition with classification"""
name: str
display_name: str
data_type: DataType
description: str = ""
classification: ClassificationLabel | None = Field(
default=None,
description="Property data classification label",
)
min_classification: ClassificationLevel | None = Field(
default=None,
description="Minimum classification level allowed for this property",
)
field_security: FieldSecurityPolicy | None = Field(default=None)
class ObjectTypeDefinition(BaseModel):
"""Object type definition with object-level classification"""
api_name: str
display_name: str
description: str
effective_classification: ClassificationLevel = Field(
default=ClassificationLevel.PUBLIC,
)
properties: dict[str, PropertyDefinition]
def compute_effective_classification(self) -> ClassificationLevel:
"""Compute effective classification (highest across all properties)"""
max_level = ClassificationLevel.PUBLIC
for prop in self.properties.values():
if prop.classification and prop.classification.level > max_level:
max_level = prop.classification.level
return max_level
#3. Automatic Classification Engine
#3.1 Rule-Based Auto-Classification
class RuleBasedClassifier:
"""Rule-based automatic classification engine"""
def __init__(self):
self._rules: list[ClassificationRule] = []
self._load_default_rules()
def _load_default_rules(self):
self._rules = [
# PII rules
ClassificationRule(
name="pii_id_number",
pattern=r"(id_card|ssn|social_security|national_id)",
field_type_match=["string"],
classification=ClassificationLevel.HIGHLY_CONFIDENTIAL,
category=DataCategory.PII,
confidence=0.95,
),
ClassificationRule(
name="pii_phone",
pattern=r"(phone|mobile|tel|cell)",
field_type_match=["string"],
classification=ClassificationLevel.CONFIDENTIAL,
category=DataCategory.PII,
confidence=0.90,
),
ClassificationRule(
name="pii_email",
pattern=r"(email|mail_address)",
field_type_match=["string"],
classification=ClassificationLevel.CONFIDENTIAL,
category=DataCategory.PII,
confidence=0.90,
),
ClassificationRule(
name="pii_name",
pattern=r"(full_name|real_name|legal_name)",
field_type_match=["string"],
classification=ClassificationLevel.INTERNAL_SENSITIVE,
category=DataCategory.PII,
confidence=0.85,
),
# Financial data rules
ClassificationRule(
name="financial_account",
pattern=r"(bank_card|account_number|routing_number)",
field_type_match=["string"],
classification=ClassificationLevel.HIGHLY_CONFIDENTIAL,
category=DataCategory.PCI,
confidence=0.95,
),
ClassificationRule(
name="financial_salary",
pattern=r"(salary|compensation|wage|pay_rate)",
field_type_match=["decimal", "float", "integer"],
classification=ClassificationLevel.CONFIDENTIAL,
category=DataCategory.FINANCIAL,
confidence=0.90,
),
# Health data rules
ClassificationRule(
name="phi_diagnosis",
pattern=r"(diagnosis|medical_record|health_condition)",
field_type_match=["string", "text"],
classification=ClassificationLevel.HIGHLY_CONFIDENTIAL,
category=DataCategory.PHI,
confidence=0.95,
),
]
def classify_field(
self,
field_name: str,
field_type: str,
description: str = "",
) -> ClassificationLabel | None:
best_match: ClassificationRule | None = None
best_confidence = 0.0
text_to_match = f"{field_name} {description}".lower()
for rule in self._rules:
if field_type not in rule.field_type_match:
continue
if re.search(rule.pattern, text_to_match, re.IGNORECASE):
if rule.confidence > best_confidence:
best_match = rule
best_confidence = rule.confidence
if best_match is None:
return None
return ClassificationLabel(
level=best_match.classification,
category=best_match.category,
labeled_by="rule_engine",
labeled_at=datetime.utcnow(),
source=LabelSource.RULE_ENGINE,
confidence=best_match.confidence,
)
#3.2 Classification Inheritance and Propagation
When data flows through the Ontology, classification labels must propagate correctly:
class ClassificationPropagator:
"""Classification label propagator"""
class PropagationPolicy(str, Enum):
HIGH_WATER_MARK = "high_water_mark"
EXPLICIT_ONLY = "explicit_only"
INHERIT_SOURCE = "inherit_source"
def propagate_on_transform(
self,
source_classifications: list[ClassificationLabel],
transform_type: str,
policy: PropagationPolicy = PropagationPolicy.HIGH_WATER_MARK,
) -> ClassificationLabel:
if policy == self.PropagationPolicy.HIGH_WATER_MARK:
max_level = max(c.level for c in source_classifications)
categories = set(c.category for c in source_classifications)
regulatory_tags = set()
for c in source_classifications:
regulatory_tags.update(c.regulatory_tags)
return ClassificationLabel(
level=max_level,
category=categories.pop() if len(categories) == 1 else DataCategory.BUSINESS,
labeled_by="propagation_engine",
labeled_at=datetime.utcnow(),
source=LabelSource.INHERITED,
confidence=min(c.confidence for c in source_classifications),
regulatory_tags=list(regulatory_tags),
)
elif policy == self.PropagationPolicy.EXPLICIT_ONLY:
raise ValueError("Explicit policy requires manual classification")
elif policy == self.PropagationPolicy.INHERIT_SOURCE:
return source_classifications[0].model_copy(
update={
"source": LabelSource.INHERITED,
"labeled_at": datetime.utcnow(),
}
)
#4. Protobuf Propagation Protocol
#4.1 Classification Label Protobuf Definition
syntax = "proto3";
package onto.classification.v1;
enum ClassificationLevel {
CLASSIFICATION_LEVEL_UNSPECIFIED = 0;
PUBLIC = 1;
INTERNAL_PUBLIC = 2;
INTERNAL_SENSITIVE = 3;
CONFIDENTIAL = 4;
HIGHLY_CONFIDENTIAL = 5;
RESTRICTED = 6;
TOP_SECRET = 7;
}
enum DataCategory {
DATA_CATEGORY_UNSPECIFIED = 0;
PII = 1;
PHI = 2;
PCI = 3;
FINANCIAL = 4;
BUSINESS = 5;
TECHNICAL = 6;
OPERATIONAL = 7;
}
message ClassificationLabel {
ClassificationLevel level = 1;
DataCategory category = 2;
string sub_category = 3;
string labeled_by = 4;
google.protobuf.Timestamp labeled_at = 5;
string source = 6;
double confidence = 7;
repeated string regulatory_tags = 8;
}
message FieldClassification {
string field_name = 1;
ClassificationLabel label = 2;
}
message ObjectClassification {
string object_type = 1;
ClassificationLevel effective_level = 2;
repeated FieldClassification field_classifications = 3;
}
#4.2 gRPC Metadata Propagation
Classification information propagates between services via gRPC metadata:
class ClassificationInterceptor(grpc.aio.UnaryUnaryClientInterceptor):
"""gRPC client interceptor - propagate classification context"""
async def intercept_unary_unary(self, continuation, client_call_details, request):
current_classification = classification_context.get()
if current_classification:
metadata = list(client_call_details.metadata or [])
metadata.append((
"x-classification-level",
str(current_classification.level.value),
))
metadata.append((
"x-classification-category",
current_classification.category.value,
))
new_details = client_call_details._replace(metadata=metadata)
return await continuation(new_details, request)
return await continuation(client_call_details, request)
#5. Integration with Security Subsystems
#5.1 Access Control Integration
Classification levels directly influence ABAC policy evaluation:
class ClassificationBasedAccessControl:
"""Classification-based access control"""
CLEARANCE_MAP = {
"public_user": ClassificationLevel.PUBLIC,
"internal_user": ClassificationLevel.INTERNAL_SENSITIVE,
"confidential_user": ClassificationLevel.CONFIDENTIAL,
"secret_user": ClassificationLevel.HIGHLY_CONFIDENTIAL,
"top_secret_user": ClassificationLevel.TOP_SECRET,
}
def check_access(
self,
user_clearance: str,
resource_classification: ClassificationLevel,
) -> AccessDecision:
max_allowed = self.CLEARANCE_MAP.get(user_clearance, ClassificationLevel.PUBLIC)
if resource_classification <= max_allowed:
return AccessDecision.ALLOW
else:
return AccessDecision.DENY
#5.2 Masking Integration
Classification levels determine masking mode selection (see S6-06 for details):
CLASSIFICATION_MASKING_MAP = {
ClassificationLevel.PUBLIC: MaskingMode.NONE,
ClassificationLevel.INTERNAL_PUBLIC: MaskingMode.NONE,
ClassificationLevel.INTERNAL_SENSITIVE: MaskingMode.PARTIAL,
ClassificationLevel.CONFIDENTIAL: MaskingMode.PARTIAL,
ClassificationLevel.HIGHLY_CONFIDENTIAL: MaskingMode.HASH,
ClassificationLevel.RESTRICTED: MaskingMode.FULL,
ClassificationLevel.TOP_SECRET: MaskingMode.FULL,
}
#5.3 Audit Integration
High-classification data access automatically triggers audit recording:
class ClassificationAuditTrigger:
"""Classification-driven audit trigger"""
AUDIT_THRESHOLDS = {
ClassificationLevel.INTERNAL_SENSITIVE: AuditLevel.BASIC,
ClassificationLevel.CONFIDENTIAL: AuditLevel.STANDARD,
ClassificationLevel.HIGHLY_CONFIDENTIAL: AuditLevel.DETAILED,
ClassificationLevel.RESTRICTED: AuditLevel.COMPREHENSIVE,
ClassificationLevel.TOP_SECRET: AuditLevel.FORENSIC,
}
async def on_data_access(
self,
classification: ClassificationLevel,
access_event: DataAccessEvent,
) -> None:
audit_level = self.AUDIT_THRESHOLDS.get(classification)
if audit_level:
await self._audit_service.record(
event=access_event,
level=audit_level,
classification=classification,
)
#6. Classification Management Workflow
#6.1 Initial Classification Process
Schema Registration → Auto-Classification Engine → Human Review → Label Activation
│ │ │ │
│ Rule matching Security team ABAC policies
│ ML classifier Compliance team Masking policies
│ Audit policies
#6.2 Classification Change Workflow
class ClassificationChangeRequest(BaseModel):
"""Classification change request"""
id: str
object_type: str
field_name: str
current_classification: ClassificationLevel
requested_classification: ClassificationLevel
reason: str
requester: str
requested_at: datetime
approvers: list[str] = Field(default_factory=list)
approval_status: ApprovalStatus = Field(default=ApprovalStatus.PENDING)
@validator("requested_classification")
def validate_downgrade(cls, v, values):
"""Downgrades require additional approval"""
current = values.get("current_classification")
if current and v < current:
values["approvers"] = ["security_team_lead", "compliance_officer"]
return v
#6.3 Periodic Review
class ClassificationReviewScheduler:
"""Classification review scheduler"""
REVIEW_INTERVALS = {
ClassificationLevel.TOP_SECRET: timedelta(days=90),
ClassificationLevel.RESTRICTED: timedelta(days=180),
ClassificationLevel.HIGHLY_CONFIDENTIAL: timedelta(days=365),
ClassificationLevel.CONFIDENTIAL: timedelta(days=365 * 2),
}
async def schedule_reviews(self) -> list[ReviewTask]:
tasks = []
for obj_type in await self._schema_registry.list_object_types():
for field_name, prop in obj_type.properties.items():
if prop.classification is None:
continue
interval = self.REVIEW_INTERVALS.get(prop.classification.level)
if interval is None:
continue
if datetime.utcnow() - prop.classification.labeled_at > interval:
tasks.append(ReviewTask(
object_type=obj_type.api_name,
field_name=field_name,
current_classification=prop.classification,
due_date=prop.classification.labeled_at + interval,
))
return tasks
#7. Testing Strategy
#7.1 Classification Correctness Tests
class TestClassification:
def test_auto_classify_pii_fields(self):
classifier = RuleBasedClassifier()
result = classifier.classify_field("ssn", "string", "Social Security Number")
assert result is not None
assert result.level == ClassificationLevel.HIGHLY_CONFIDENTIAL
assert result.category == DataCategory.PII
def test_propagation_high_water_mark(self):
propagator = ClassificationPropagator()
sources = [
make_label(ClassificationLevel.INTERNAL_SENSITIVE),
make_label(ClassificationLevel.CONFIDENTIAL),
]
result = propagator.propagate_on_transform(sources, "join")
assert result.level == ClassificationLevel.CONFIDENTIAL
def test_object_effective_classification(self):
obj = ObjectTypeDefinition(
api_name="Customer",
display_name="Customer",
description="Customer object",
properties={
"name": PropertyDefinition(
name="name", display_name="Name", data_type=DataType.STRING,
classification=make_label(ClassificationLevel.INTERNAL_SENSITIVE),
),
"ssn": PropertyDefinition(
name="ssn", display_name="SSN", data_type=DataType.STRING,
classification=make_label(ClassificationLevel.HIGHLY_CONFIDENTIAL),
),
},
)
assert obj.compute_effective_classification() == ClassificationLevel.HIGHLY_CONFIDENTIAL
#8. Production Best Practices
#8.1 Classification Governance Principles
- Default conservative: Unclassified fields default to B1 (Internal Sensitive), not Public
- High-water mark: After data combination or transformation, adopt the highest classification from all sources
- Periodic review: C1 and above reviewed at least annually; D-level reviewed quarterly
- Change auditing: All classification changes logged in audit trail; downgrades require dual approval
#8.2 Regulatory Mapping
| Regulation | Relevant Data Category | Recommended Minimum Classification |
|---|---|---|
| GDPR | PII | B2 (Confidential) |
| HIPAA | PHI | C1 (Highly Confidential) |
| PCI DSS | PCI | C1 (Highly Confidential) |
| CCPA | PII | B2 (Confidential) |
| SOX | Financial | B2 (Confidential) |
#8.3 Operational Recommendations
- Automatically trigger classification engine when new Ontology object types are added
- Classification results require human review before serving as ABAC policy inputs
- Establish classification dashboards monitoring distribution and access patterns per tier
- Set up alert notifications for classification changes on C2/D-level data
#9. Summary
The coomia-dip 7-tier data classification system provides a unified metadata foundation for security subsystems through fine-grained classification levels. Key design highlights:
- 7-tier granularity: Covers the complete sensitivity spectrum from public to top secret
- Auto-classification: Rule engine + ML classifier achieve high-confidence automatic labeling
- Propagation mechanism: Classification labels propagate automatically with data flow, ensuring consistency
- System integration: Deep integration with access control, dynamic masking, and audit subsystems
- Compliance alignment: Built-in regulatory mappings for GDPR, HIPAA, PCI DSS, and more
The next article will explore the coomia-dip audit trail system and its 13 audit event types.