HIPAA-Ready Healthcare API
Build MediRecord — a patient record API with role-based field masking, break-glass access, audit trails, and fail-closed enforcement. Go SDK + Gin.
HIPAA-Ready Healthcare API
What you'll build: MediRecord — a patient record REST API for a telehealth platform. Three user types access the same records with different visibility:
| Role | Can see |
|---|---|
patient | Their own records only, no SSN/DOB in responses |
clinician | Any patient in their practice, full clinical data |
admin | Everything including audit logs — requires MFA claim |
break_glass | Emergency override — all fields, always audited with reason |
CoreSDK enforces role gating, PII masking per role, break-glass audit, and fail-closed mode (no sidecar = no access — healthcare data is too sensitive for fail-open).
The Story
MedCore Health runs a multi-practice telehealth platform. HIPAA requires:
- Minimum necessary access — patients see only their own data
- Audit log for every access to PHI (Protected Health Information)
- Break-glass procedure — emergency access with mandatory reason, always flagged
- Fail-closed — any auth failure denies access, never leaks
Without CoreSDK: field masking, audit, and fail-closed logic scattered across models, middleware, and ad-hoc code. With CoreSDK: one middleware, one policy file.
Architecture
Clinician / Patient / Admin
│ JWT (contains role + practice_id + mfa_verified)
▼
┌─────────────────────────────────┐
│ Gin HTTP Server │
│ │
│ CoreSDK Auth Middleware │ ← fail-CLOSED (no sidecar = 401)
│ │ │
│ ▼ │
│ Role extraction from claims │
│ │ │
│ ┌────┴──────────────────────┐ │
│ │ Field masking per role │ │ ← patient sees masked SSN
│ │ patient → mask SSN, DOB │ │
│ │ clinician → full record │ │
│ │ break_glass → log reason │ │
│ └───────────────────────────┘ │
│ │ │
│ Audit every PHI access │
└─────────────────────────────────┘
│ gRPC :50051 (fail-CLOSED)
▼
┌─────────────────┐
│ CoreSDK Sidecar│
└─────────────────┘Prerequisites
go get github.com/coresdk-dev/sdk-go
go get github.com/gin-gonic/ginQuickstart
git clone https://github.com/coresdk-dev/examples
cd examples/go/medirecord
CORESDK_FAIL_MODE=closed go run main.goCode Walkthrough
Step 1 — Fail-closed SDK (healthcare default)
package main
import (
"context"
"os"
coresdk "github.com/coresdk-dev/sdk-go"
"github.com/gin-gonic/gin"
)
func main() {
// FAIL-CLOSED: sidecar unreachable = deny all requests
// Never use fail-open for healthcare data
os.Setenv("CORESDK_FAIL_MODE", "closed")
sdk, err := coresdk.FromEnv()
if err != nil {
// In fail-closed mode, this error means sidecar is unreachable.
// Do NOT start the server — returning 500 is safer than serving unauthed.
panic("sidecar unreachable at startup (fail-closed): " + err.Error())
}
r := gin.New()
r.Use(AuthMiddleware(sdk))
r.GET("/patients/:id", GetPatient)
r.GET("/patients/:id/audit", RequireRole("admin"), GetAuditLog)
r.Run(":8080")
}Step 2 — Auth middleware with break-glass detection
func AuthMiddleware(sdk *coresdk.SDK) gin.HandlerFunc {
return func(c *gin.Context) {
token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
// Fail-closed: any error denies access
claims, err := sdk.Authorize(context.Background(), token)
if err != nil {
c.AbortWithStatusJSON(401, gin.H{
"type": "https://medirecord.example/errors/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Valid JWT required. Sidecar validation failed.",
})
return
}
c.Set("claims", claims)
c.Set("roles", claims.Roles)
c.Set("practice_id", claims.TenantID)
// Detect break-glass: role present but requires mandatory reason header
if contains(claims.Roles, "break_glass") {
reason := c.GetHeader("X-Break-Glass-Reason")
if reason == "" {
c.AbortWithStatusJSON(403, gin.H{
"type": "https://medirecord.example/errors/break-glass-reason-required",
"title": "Forbidden",
"status": 403,
"detail": "Break-glass access requires X-Break-Glass-Reason header.",
})
return
}
c.Set("break_glass_reason", reason)
AuditBreakGlass(claims, c.Request.URL.Path, reason)
}
c.Next()
}
}Step 3 — Field masking per role
type PatientRecord struct {
ID string `json:"id"`
Name string `json:"name"`
SSN string `json:"ssn,omitempty"`
DOB string `json:"dob,omitempty"`
Diagnosis string `json:"diagnosis,omitempty"`
PracticeID string `json:"practice_id"`
}
// MaskForRole removes fields the caller's role is not allowed to see.
func MaskForRole(p PatientRecord, roles []string) PatientRecord {
masked := p
isPatient := contains(roles, "patient")
isClinician := contains(roles, "clinician")
isBreakGlass := contains(roles, "break_glass")
if isBreakGlass {
// break_glass sees everything — already audited in middleware
return masked
}
if isPatient && !isClinician {
masked.SSN = mask(p.SSN) // "***-**-6789"
masked.DOB = mask(p.DOB) // "****-**-15"
masked.Diagnosis = "" // not visible to patient
}
return masked
}
func mask(s string) string {
if len(s) <= 4 {
return strings.Repeat("*", len(s))
}
return strings.Repeat("*", len(s)-4) + s[len(s)-4:]
}Step 4 — Tenant-scoped record access
func GetPatient(c *gin.Context) {
claims := c.MustGet("claims").(*coresdk.Claims)
roles := c.MustGet("roles").([]string)
practiceID := c.MustGet("practice_id").(string)
patientID := c.Param("id")
record, ok := db[patientID]
if !ok {
c.JSON(404, gin.H{"type": "...not-found", "status": 404})
return
}
// Tenant isolation: patients can only see their own practice's records
if record.PracticeID != practiceID && !contains(roles, "break_glass") {
c.JSON(403, gin.H{
"type": "https://medirecord.example/errors/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Patient record belongs to a different practice.",
})
return
}
// Patient self-access check
if contains(roles, "patient") && !contains(roles, "clinician") {
if record.ID != claims.Subject {
c.JSON(403, gin.H{
"type": "https://medirecord.example/errors/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Patients may only access their own records.",
})
return
}
}
// Audit PHI access
AuditPHIAccess(claims, patientID, "read")
// Mask fields based on caller's role
c.JSON(200, MaskForRole(record, roles))
}Step 5 — Audit log
type AuditEvent struct {
Timestamp string `json:"timestamp"`
Actor string `json:"actor"`
Action string `json:"action"`
Resource string `json:"resource"`
Outcome string `json:"outcome"`
Reason string `json:"reason,omitempty"`
BreakGlass bool `json:"break_glass,omitempty"`
}
var auditLog []AuditEvent
func AuditPHIAccess(claims *coresdk.Claims, patientID, action string) {
auditLog = append(auditLog, AuditEvent{
Timestamp: time.Now().UTC().Format(time.RFC3339),
Actor: claims.Subject,
Action: action,
Resource: "patient/" + patientID,
Outcome: "success",
})
}
func AuditBreakGlass(claims *coresdk.Claims, path, reason string) {
auditLog = append(auditLog, AuditEvent{
Timestamp: time.Now().UTC().Format(time.RFC3339),
Actor: claims.Subject,
Action: "break_glass",
Resource: path,
Outcome: "access_granted",
Reason: reason,
BreakGlass: true,
})
// In production: page the on-call security team immediately
log.Printf("BREAK GLASS: actor=%s path=%s reason=%q", claims.Subject, path, reason)
}Testing Break-Glass Access
# Normal clinician access
curl http://localhost:8080/patients/p-001 \
-H "Authorization: Bearer clinician-token"
# {"id":"p-001","name":"Alice Smith","ssn":"123-45-6789","diagnosis":"..."}
# Patient self-access — SSN masked
curl http://localhost:8080/patients/p-001 \
-H "Authorization: Bearer patient-token"
# {"id":"p-001","name":"Alice Smith","ssn":"***-**-6789","diagnosis":""}
# Break-glass — requires reason header
curl http://localhost:8080/patients/p-001 \
-H "Authorization: Bearer break-glass-token" \
-H "X-Break-Glass-Reason: Patient in ER, consent unreachable"
# Full record + audit event written + on-call paged
# Break-glass without reason → 403
curl http://localhost:8080/patients/p-001 \
-H "Authorization: Bearer break-glass-token"
# {"status":403,"detail":"Break-glass access requires X-Break-Glass-Reason header."}HIPAA Compliance Notes
| Requirement | Implementation |
|---|---|
| Minimum necessary | Field masking per role in MaskForRole() |
| Access audit | AuditPHIAccess() on every PHI read |
| Break-glass procedure | X-Break-Glass-Reason header + immediate audit |
| Fail-closed | CORESDK_FAIL_MODE=closed — sidecar down = no access |
| Tenant isolation | practice_id from JWT, validated on every record access |
Environment Variables
| Variable | Required | Description |
|---|---|---|
CORESDK_SIDECAR_ADDR | Yes | Sidecar gRPC — must be reachable at startup |
CORESDK_FAIL_MODE | Yes | Must be closed for PHI workloads |
CORESDK_JWKS_URI | Yes | Your IdP JWKS — validates role claims cryptographically |
CORESDK_TENANT_ID | Yes | Practice ID for default tenant scope |
Full Source
cd examples/go/medirecord
CORESDK_FAIL_MODE=closed go run main.goFastAPI Integration
Complete guide to adding CoreSDK to a FastAPI application — auth, RBAC, ABAC, multi-tenancy, tracing, and RFC 9457 errors.
Fintech Transaction API
Build LedgerAPI — a double-entry ledger with per-transaction policy enforcement, amount-based approval workflows, SOC 2 audit trail, and idempotency. Java + Spring Boot.