1115 lines
23 KiB
Markdown
1115 lines
23 KiB
Markdown
|
|
# Phoenix Operating Model - API Specification
|
||
|
|
|
||
|
|
**Complete API specification for all five control planes**
|
||
|
|
|
||
|
|
This document provides detailed API specifications for the Phoenix operating model, including GraphQL schemas, REST endpoints, and integration patterns.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Executive Summary
|
||
|
|
|
||
|
|
The Phoenix operating model exposes APIs for all five control planes:
|
||
|
|
1. **Commercial Plane API** - Client and billing operations
|
||
|
|
2. **Tenancy Plane API** - Tenant and identity operations
|
||
|
|
3. **Subscription Plane API** - Subscription and quota operations
|
||
|
|
4. **Environment Plane API** - Environment and deployment operations
|
||
|
|
5. **Content & DevOps Plane API** - Content and Git operations
|
||
|
|
|
||
|
|
All APIs support:
|
||
|
|
- GraphQL interface (primary)
|
||
|
|
- REST interface (alternative)
|
||
|
|
- Authentication via Keycloak
|
||
|
|
- Authorization via RBAC
|
||
|
|
- Audit logging
|
||
|
|
- Multi-region support
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## I. Commercial Plane API
|
||
|
|
|
||
|
|
### GraphQL Schema
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Client Management
|
||
|
|
type Client {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
legalEntity: LegalEntity!
|
||
|
|
contract: Contract
|
||
|
|
msa: MSA
|
||
|
|
invoicingConfig: InvoicingConfig!
|
||
|
|
paymentInstruments: [PaymentInstrument!]!
|
||
|
|
costCenters: [CostCenter!]!
|
||
|
|
tenants: [Tenant!]!
|
||
|
|
usageAggregation: UsageAggregation
|
||
|
|
chargebackRules: [ChargebackRule!]!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type LegalEntity {
|
||
|
|
name: String!
|
||
|
|
registrationNumber: String
|
||
|
|
jurisdiction: String!
|
||
|
|
taxId: String
|
||
|
|
address: Address!
|
||
|
|
}
|
||
|
|
|
||
|
|
type InvoicingConfig {
|
||
|
|
format: InvoiceFormat!
|
||
|
|
frequency: InvoiceFrequency!
|
||
|
|
currency: String!
|
||
|
|
paymentTerms: String!
|
||
|
|
billingAddress: Address!
|
||
|
|
emailRecipients: [String!]!
|
||
|
|
}
|
||
|
|
|
||
|
|
enum InvoiceFormat {
|
||
|
|
PDF
|
||
|
|
XML
|
||
|
|
JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
enum InvoiceFrequency {
|
||
|
|
MONTHLY
|
||
|
|
QUARTERLY
|
||
|
|
ANNUAL
|
||
|
|
}
|
||
|
|
|
||
|
|
# Queries
|
||
|
|
type Query {
|
||
|
|
client(id: ID!): Client
|
||
|
|
clients(filter: ClientFilter): [Client!]!
|
||
|
|
billing(clientId: ID!, timeRange: TimeRange!): BillingData!
|
||
|
|
invoices(clientId: ID!, filter: InvoiceFilter): [Invoice!]!
|
||
|
|
costCenters(clientId: ID!): [CostCenter!]!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mutations
|
||
|
|
type Mutation {
|
||
|
|
createClient(input: CreateClientInput!): Client!
|
||
|
|
updateClient(id: ID!, input: UpdateClientInput!): Client!
|
||
|
|
deleteClient(id: ID!): Boolean!
|
||
|
|
createInvoice(clientId: ID!, period: BillingPeriod!): Invoice!
|
||
|
|
processPayment(invoiceId: ID!, payment: PaymentInput!): Payment!
|
||
|
|
addPaymentInstrument(clientId: ID!, instrument: PaymentInstrumentInput!): PaymentInstrument!
|
||
|
|
removePaymentInstrument(clientId: ID!, instrumentId: ID!): Boolean!
|
||
|
|
createCostCenter(clientId: ID!, input: CreateCostCenterInput!): CostCenter!
|
||
|
|
updateCostCenter(clientId: ID!, costCenterId: ID!, input: UpdateCostCenterInput!): CostCenter!
|
||
|
|
deleteCostCenter(clientId: ID!, costCenterId: ID!): Boolean!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Input Types
|
||
|
|
input CreateClientInput {
|
||
|
|
name: String!
|
||
|
|
legalEntity: LegalEntityInput!
|
||
|
|
invoicingConfig: InvoicingConfigInput!
|
||
|
|
contract: ContractInput
|
||
|
|
msa: MSAInput
|
||
|
|
}
|
||
|
|
|
||
|
|
input ClientFilter {
|
||
|
|
name: String
|
||
|
|
jurisdiction: String
|
||
|
|
status: ClientStatus
|
||
|
|
}
|
||
|
|
|
||
|
|
input TimeRange {
|
||
|
|
start: DateTime!
|
||
|
|
end: DateTime!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Client Management
|
||
|
|
POST /api/v1/commercial/clients
|
||
|
|
GET /api/v1/commercial/clients
|
||
|
|
GET /api/v1/commercial/clients/{id}
|
||
|
|
PUT /api/v1/commercial/clients/{id}
|
||
|
|
DELETE /api/v1/commercial/clients/{id}
|
||
|
|
|
||
|
|
# Billing
|
||
|
|
GET /api/v1/commercial/clients/{id}/billing
|
||
|
|
GET /api/v1/commercial/clients/{id}/invoices
|
||
|
|
POST /api/v1/commercial/clients/{id}/invoices
|
||
|
|
GET /api/v1/commercial/invoices/{id}
|
||
|
|
POST /api/v1/commercial/invoices/{id}/payments
|
||
|
|
|
||
|
|
# Cost Centers
|
||
|
|
POST /api/v1/commercial/clients/{id}/cost-centers
|
||
|
|
GET /api/v1/commercial/clients/{id}/cost-centers
|
||
|
|
PUT /api/v1/commercial/clients/{id}/cost-centers/{costCenterId}
|
||
|
|
DELETE /api/v1/commercial/clients/{id}/cost-centers/{costCenterId}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Queries
|
||
|
|
|
||
|
|
#### Create Client
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createClient(input: {
|
||
|
|
name: "Government Agency A"
|
||
|
|
legalEntity: {
|
||
|
|
name: "Government Agency A"
|
||
|
|
jurisdiction: "Nation A"
|
||
|
|
address: {
|
||
|
|
street: "123 Main St"
|
||
|
|
city: "Capital City"
|
||
|
|
country: "Nation A"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
invoicingConfig: {
|
||
|
|
format: PDF
|
||
|
|
frequency: MONTHLY
|
||
|
|
currency: "USD"
|
||
|
|
paymentTerms: "Net 30"
|
||
|
|
billingAddress: {
|
||
|
|
street: "123 Main St"
|
||
|
|
city: "Capital City"
|
||
|
|
country: "Nation A"
|
||
|
|
}
|
||
|
|
emailRecipients: ["billing@agency.gov"]
|
||
|
|
}
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
legalEntity {
|
||
|
|
name
|
||
|
|
jurisdiction
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Get Billing Data
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
query {
|
||
|
|
billing(
|
||
|
|
clientId: "client-id"
|
||
|
|
timeRange: {
|
||
|
|
start: "2025-01-01T00:00:00Z"
|
||
|
|
end: "2025-01-31T23:59:59Z"
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
totalCost
|
||
|
|
currency
|
||
|
|
bySubscription {
|
||
|
|
subscription {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
cost
|
||
|
|
percentage
|
||
|
|
}
|
||
|
|
byCostCenter {
|
||
|
|
costCenter {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
cost
|
||
|
|
percentage
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## II. Tenancy Plane API
|
||
|
|
|
||
|
|
### GraphQL Schema
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Tenant Management
|
||
|
|
type Tenant {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
primaryDomains: [String!]!
|
||
|
|
identityProvider: IdentityProvider!
|
||
|
|
rbacNamespace: String!
|
||
|
|
dataResidencyFlags: [DataResidencyFlag!]!
|
||
|
|
complianceProfile: ComplianceProfile!
|
||
|
|
client: Client!
|
||
|
|
subscriptions: [Subscription!]!
|
||
|
|
environments: [Environment!]!
|
||
|
|
regions: [Region!]!
|
||
|
|
keycloakRealmId: String
|
||
|
|
multiRegionEnabled: Boolean!
|
||
|
|
regionalDataResidency: [RegionalDataResidency!]!
|
||
|
|
crossBorderGovernance: CrossBorderGovernance
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type IdentityProvider {
|
||
|
|
type: IdentityProviderType!
|
||
|
|
config: JSON!
|
||
|
|
ssoEnabled: Boolean!
|
||
|
|
mfaRequired: Boolean!
|
||
|
|
}
|
||
|
|
|
||
|
|
enum IdentityProviderType {
|
||
|
|
KEYCLOAK
|
||
|
|
AZURE_AD
|
||
|
|
OKTA
|
||
|
|
GOOGLE_WORKSPACE
|
||
|
|
CUSTOM_SAML
|
||
|
|
CUSTOM_OIDC
|
||
|
|
}
|
||
|
|
|
||
|
|
# Queries
|
||
|
|
type Query {
|
||
|
|
tenant(id: ID!): Tenant
|
||
|
|
tenants(filter: TenantFilter): [Tenant!]!
|
||
|
|
tenantByDomain(domain: String!): Tenant
|
||
|
|
identityProvider(tenantId: ID!): IdentityProvider
|
||
|
|
complianceProfile(tenantId: ID!): ComplianceProfile
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mutations
|
||
|
|
type Mutation {
|
||
|
|
createTenant(input: CreateTenantInput!): Tenant!
|
||
|
|
updateTenant(id: ID!, input: UpdateTenantInput!): Tenant!
|
||
|
|
deleteTenant(id: ID!): Boolean!
|
||
|
|
configureIdentityProvider(tenantId: ID!, provider: IdentityProviderInput!): IdentityProvider!
|
||
|
|
syncKeycloakRealm(tenantId: ID!): KeycloakRealm!
|
||
|
|
updateComplianceProfile(tenantId: ID!, profile: ComplianceProfileInput!): ComplianceProfile!
|
||
|
|
addDataResidencyFlag(tenantId: ID!, flag: DataResidencyFlagInput!): DataResidencyFlag!
|
||
|
|
removeDataResidencyFlag(tenantId: ID!, flagId: ID!): Boolean!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Input Types
|
||
|
|
input CreateTenantInput {
|
||
|
|
name: String!
|
||
|
|
clientId: ID!
|
||
|
|
primaryDomains: [String!]!
|
||
|
|
identityProvider: IdentityProviderInput!
|
||
|
|
rbacNamespace: String!
|
||
|
|
complianceProfile: ComplianceProfileInput!
|
||
|
|
dataResidencyFlags: [DataResidencyFlagInput!]
|
||
|
|
multiRegionEnabled: Boolean
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Tenant Management
|
||
|
|
POST /api/v1/tenancy/tenants
|
||
|
|
GET /api/v1/tenancy/tenants
|
||
|
|
GET /api/v1/tenancy/tenants/{id}
|
||
|
|
PUT /api/v1/tenancy/tenants/{id}
|
||
|
|
DELETE /api/v1/tenancy/tenants/{id}
|
||
|
|
GET /api/v1/tenancy/tenants/by-domain/{domain}
|
||
|
|
|
||
|
|
# Identity Provider
|
||
|
|
GET /api/v1/tenancy/tenants/{id}/identity-provider
|
||
|
|
PUT /api/v1/tenancy/tenants/{id}/identity-provider
|
||
|
|
POST /api/v1/tenancy/tenants/{id}/keycloak/sync
|
||
|
|
|
||
|
|
# Compliance
|
||
|
|
GET /api/v1/tenancy/tenants/{id}/compliance
|
||
|
|
PUT /api/v1/tenancy/tenants/{id}/compliance
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Queries
|
||
|
|
|
||
|
|
#### Create Tenant
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createTenant(input: {
|
||
|
|
name: "agency-tenant"
|
||
|
|
clientId: "client-id"
|
||
|
|
primaryDomains: ["agency.gov", "agency.sankofa.nexus"]
|
||
|
|
identityProvider: {
|
||
|
|
type: KEYCLOAK
|
||
|
|
config: {}
|
||
|
|
ssoEnabled: true
|
||
|
|
mfaRequired: true
|
||
|
|
}
|
||
|
|
rbacNamespace: "agency-namespace"
|
||
|
|
complianceProfile: {
|
||
|
|
standards: [ISO_27001, SOC_2, FEDRAMP]
|
||
|
|
}
|
||
|
|
dataResidencyFlags: [{
|
||
|
|
region: "region-a"
|
||
|
|
requirement: REQUIRED
|
||
|
|
enforcement: HARD
|
||
|
|
}]
|
||
|
|
multiRegionEnabled: true
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
primaryDomains
|
||
|
|
keycloakRealmId
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Configure Identity Provider
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
configureIdentityProvider(
|
||
|
|
tenantId: "tenant-id"
|
||
|
|
provider: {
|
||
|
|
type: AZURE_AD
|
||
|
|
config: {
|
||
|
|
tenantId: "azure-tenant-id"
|
||
|
|
clientId: "azure-client-id"
|
||
|
|
clientSecret: "azure-client-secret"
|
||
|
|
}
|
||
|
|
ssoEnabled: true
|
||
|
|
mfaRequired: true
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
type
|
||
|
|
ssoEnabled
|
||
|
|
mfaRequired
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## III. Subscription Plane API
|
||
|
|
|
||
|
|
### GraphQL Schema
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Subscription Management
|
||
|
|
type Subscription {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
tenant: Tenant!
|
||
|
|
client: Client!
|
||
|
|
type: SubscriptionType!
|
||
|
|
serviceBundles: [ServiceBundle!]!
|
||
|
|
quotas: Quotas!
|
||
|
|
limits: Limits!
|
||
|
|
costTracking: CostTracking!
|
||
|
|
policyPacks: [PolicyPack!]!
|
||
|
|
featureEntitlements: [FeatureEntitlement!]!
|
||
|
|
environments: [Environment!]!
|
||
|
|
regions: [Region!]!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
enum SubscriptionType {
|
||
|
|
SHARED_PLATFORM
|
||
|
|
PRODUCT
|
||
|
|
SANDBOX
|
||
|
|
INNOVATION
|
||
|
|
}
|
||
|
|
|
||
|
|
type ServiceBundle {
|
||
|
|
service: ServiceType!
|
||
|
|
enabled: Boolean!
|
||
|
|
quotas: ServiceQuotas
|
||
|
|
limits: ServiceLimits
|
||
|
|
}
|
||
|
|
|
||
|
|
enum ServiceType {
|
||
|
|
COMPUTE
|
||
|
|
STORAGE
|
||
|
|
NETWORKING
|
||
|
|
DATABASE
|
||
|
|
AI_ML
|
||
|
|
ANALYTICS
|
||
|
|
SECURITY
|
||
|
|
MONITORING
|
||
|
|
BACKUP
|
||
|
|
}
|
||
|
|
|
||
|
|
# Queries
|
||
|
|
type Query {
|
||
|
|
subscription(id: ID!): Subscription
|
||
|
|
subscriptions(filter: SubscriptionFilter): [Subscription!]!
|
||
|
|
quotas(subscriptionId: ID!): Quotas!
|
||
|
|
checkQuota(subscriptionId: ID!, resource: ResourceType!): QuotaStatus!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mutations
|
||
|
|
type Mutation {
|
||
|
|
createSubscription(input: CreateSubscriptionInput!): Subscription!
|
||
|
|
updateSubscription(id: ID!, input: UpdateSubscriptionInput!): Subscription!
|
||
|
|
deleteSubscription(id: ID!): Boolean!
|
||
|
|
updateQuotas(subscriptionId: ID!, quotas: QuotasInput!): Quotas!
|
||
|
|
addServiceBundle(subscriptionId: ID!, bundle: ServiceBundleInput!): ServiceBundle!
|
||
|
|
removeServiceBundle(subscriptionId: ID!, service: ServiceType!): Boolean!
|
||
|
|
addPolicyPack(subscriptionId: ID!, pack: PolicyPackInput!): PolicyPack!
|
||
|
|
removePolicyPack(subscriptionId: ID!, packId: ID!): Boolean!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Subscription Management
|
||
|
|
POST /api/v1/subscription/subscriptions
|
||
|
|
GET /api/v1/subscription/subscriptions
|
||
|
|
GET /api/v1/subscription/subscriptions/{id}
|
||
|
|
PUT /api/v1/subscription/subscriptions/{id}
|
||
|
|
DELETE /api/v1/subscription/subscriptions/{id}
|
||
|
|
|
||
|
|
# Quotas
|
||
|
|
GET /api/v1/subscription/subscriptions/{id}/quotas
|
||
|
|
PUT /api/v1/subscription/subscriptions/{id}/quotas
|
||
|
|
GET /api/v1/subscription/subscriptions/{id}/quotas/check
|
||
|
|
|
||
|
|
# Service Bundles
|
||
|
|
POST /api/v1/subscription/subscriptions/{id}/service-bundles
|
||
|
|
DELETE /api/v1/subscription/subscriptions/{id}/service-bundles/{service}
|
||
|
|
|
||
|
|
# Policy Packs
|
||
|
|
POST /api/v1/subscription/subscriptions/{id}/policy-packs
|
||
|
|
DELETE /api/v1/subscription/subscriptions/{id}/policy-packs/{packId}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Queries
|
||
|
|
|
||
|
|
#### Create Subscription
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createSubscription(input: {
|
||
|
|
name: "production-subscription"
|
||
|
|
tenantId: "tenant-id"
|
||
|
|
type: PRODUCT
|
||
|
|
serviceBundles: [{
|
||
|
|
service: COMPUTE
|
||
|
|
enabled: true
|
||
|
|
quotas: {
|
||
|
|
vcpu: 100
|
||
|
|
memory: 512
|
||
|
|
instances: 50
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
service: STORAGE
|
||
|
|
enabled: true
|
||
|
|
quotas: {
|
||
|
|
total: 10000
|
||
|
|
perInstance: 500
|
||
|
|
}
|
||
|
|
}]
|
||
|
|
quotas: {
|
||
|
|
compute: {
|
||
|
|
vcpu: 100
|
||
|
|
memory: 512
|
||
|
|
instances: 50
|
||
|
|
}
|
||
|
|
storage: {
|
||
|
|
total: 10000
|
||
|
|
perInstance: 500
|
||
|
|
}
|
||
|
|
}
|
||
|
|
policyPacks: [{
|
||
|
|
name: "security-policy"
|
||
|
|
type: SECURITY
|
||
|
|
policies: []
|
||
|
|
enforcement: HARD
|
||
|
|
}]
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
type
|
||
|
|
serviceBundles {
|
||
|
|
service
|
||
|
|
enabled
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## IV. Environment Plane API
|
||
|
|
|
||
|
|
### GraphQL Schema
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Environment Management
|
||
|
|
type Environment {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
type: EnvironmentType!
|
||
|
|
subscription: Subscription!
|
||
|
|
networkIsolation: NetworkIsolation!
|
||
|
|
dataIsolation: DataIsolation!
|
||
|
|
deploymentPolicies: [DeploymentPolicy!]!
|
||
|
|
runtimeSecrets: [Secret!]!
|
||
|
|
complianceOverlays: [ComplianceOverlay!]!
|
||
|
|
region: Region
|
||
|
|
promotionFlow: PromotionFlow
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
enum EnvironmentType {
|
||
|
|
DEV
|
||
|
|
INT
|
||
|
|
UAT
|
||
|
|
STAGING
|
||
|
|
PROD
|
||
|
|
REGULATED
|
||
|
|
SOVEREIGN
|
||
|
|
AIR_GAPPED
|
||
|
|
}
|
||
|
|
|
||
|
|
# Queries
|
||
|
|
type Query {
|
||
|
|
environment(id: ID!): Environment
|
||
|
|
environments(filter: EnvironmentFilter): [Environment!]!
|
||
|
|
promotionFlow(environmentId: ID!): PromotionFlow
|
||
|
|
deploymentPolicies(environmentId: ID!): [DeploymentPolicy!]!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mutations
|
||
|
|
type Mutation {
|
||
|
|
createEnvironment(input: CreateEnvironmentInput!): Environment!
|
||
|
|
updateEnvironment(id: ID!, input: UpdateEnvironmentInput!): Environment!
|
||
|
|
deleteEnvironment(id: ID!): Boolean!
|
||
|
|
promoteArtifact(input: PromoteArtifactInput!): PromotionResult!
|
||
|
|
approvePromotion(promotionId: ID!, approved: Boolean!): PromotionResult!
|
||
|
|
configurePromotionFlow(environmentId: ID!, flow: PromotionFlowInput!): PromotionFlow!
|
||
|
|
addDeploymentPolicy(environmentId: ID!, policy: DeploymentPolicyInput!): DeploymentPolicy!
|
||
|
|
removeDeploymentPolicy(environmentId: ID!, policyId: ID!): Boolean!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Environment Management
|
||
|
|
POST /api/v1/environment/environments
|
||
|
|
GET /api/v1/environment/environments
|
||
|
|
GET /api/v1/environment/environments/{id}
|
||
|
|
PUT /api/v1/environment/environments/{id}
|
||
|
|
DELETE /api/v1/environment/environments/{id}
|
||
|
|
|
||
|
|
# Promotion
|
||
|
|
POST /api/v1/environment/promotions
|
||
|
|
GET /api/v1/environment/promotions/{id}
|
||
|
|
POST /api/v1/environment/promotions/{id}/approve
|
||
|
|
POST /api/v1/environment/promotions/{id}/reject
|
||
|
|
|
||
|
|
# Deployment Policies
|
||
|
|
POST /api/v1/environment/environments/{id}/deployment-policies
|
||
|
|
GET /api/v1/environment/environments/{id}/deployment-policies
|
||
|
|
DELETE /api/v1/environment/environments/{id}/deployment-policies/{policyId}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Queries
|
||
|
|
|
||
|
|
#### Create Environment
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createEnvironment(input: {
|
||
|
|
name: "production-env"
|
||
|
|
subscriptionId: "subscription-id"
|
||
|
|
type: PROD
|
||
|
|
networkIsolation: {
|
||
|
|
vpcId: "vpc-id"
|
||
|
|
subnetIds: ["subnet-1", "subnet-2"]
|
||
|
|
firewallRules: []
|
||
|
|
}
|
||
|
|
dataIsolation: {
|
||
|
|
encryptionAtRest: true
|
||
|
|
encryptionInTransit: true
|
||
|
|
accessControls: []
|
||
|
|
}
|
||
|
|
deploymentPolicies: [{
|
||
|
|
name: "production-policy"
|
||
|
|
type: POLICY_DRIVEN
|
||
|
|
approvalRequired: true
|
||
|
|
approvers: ["release-manager-1", "release-manager-2"]
|
||
|
|
}]
|
||
|
|
region: "region-a"
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
type
|
||
|
|
region {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Promote Artifact
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
promoteArtifact(input: {
|
||
|
|
artifactId: "artifact-id"
|
||
|
|
fromEnvironmentId: "staging-env-id"
|
||
|
|
toEnvironmentId: "prod-env-id"
|
||
|
|
metadata: {
|
||
|
|
version: "1.2.3"
|
||
|
|
changelog: "Production release"
|
||
|
|
}
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
status
|
||
|
|
approvalRequired
|
||
|
|
approvers
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## V. Content & DevOps Plane API
|
||
|
|
|
||
|
|
### GraphQL Schema
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Content Hierarchy
|
||
|
|
type Enterprise {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
portfolios: [Portfolio!]!
|
||
|
|
ownership: Ownership!
|
||
|
|
governance: Governance!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type Portfolio {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
enterprise: Enterprise!
|
||
|
|
products: [Product!]!
|
||
|
|
ownership: Ownership!
|
||
|
|
governance: Governance!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type Product {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
portfolio: Portfolio!
|
||
|
|
applications: [Application!]!
|
||
|
|
ownership: Ownership!
|
||
|
|
governance: Governance!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type Application {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
product: Product!
|
||
|
|
components: [Component!]!
|
||
|
|
gitRepos: [GitRepo!]!
|
||
|
|
ownership: Ownership!
|
||
|
|
governance: Governance!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
type Component {
|
||
|
|
id: ID!
|
||
|
|
name: String!
|
||
|
|
application: Application!
|
||
|
|
contentType: ContentType!
|
||
|
|
content: Content!
|
||
|
|
ownership: Ownership!
|
||
|
|
governance: Governance!
|
||
|
|
version: String!
|
||
|
|
lineage: [LineageEntry!]!
|
||
|
|
createdAt: DateTime!
|
||
|
|
updatedAt: DateTime!
|
||
|
|
metadata: JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
enum ContentType {
|
||
|
|
SOURCE_CODE
|
||
|
|
IAC
|
||
|
|
PIPELINE
|
||
|
|
CONFIG_TEMPLATE
|
||
|
|
DOCUMENTATION
|
||
|
|
DATA_SCHEMA
|
||
|
|
AI_MODEL
|
||
|
|
PROMPT
|
||
|
|
}
|
||
|
|
|
||
|
|
# Queries
|
||
|
|
type Query {
|
||
|
|
enterprise(id: ID!): Enterprise
|
||
|
|
enterprises: [Enterprise!]!
|
||
|
|
portfolio(id: ID!): Portfolio
|
||
|
|
product(id: ID!): Product
|
||
|
|
application(id: ID!): Application
|
||
|
|
component(id: ID!): Component
|
||
|
|
gitRepo(id: ID!): GitRepo
|
||
|
|
gitRepos(filter: GitRepoFilter): [GitRepo!]!
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mutations
|
||
|
|
type Mutation {
|
||
|
|
createEnterprise(input: CreateEnterpriseInput!): Enterprise!
|
||
|
|
createPortfolio(input: CreatePortfolioInput!): Portfolio!
|
||
|
|
createProduct(input: CreateProductInput!): Product!
|
||
|
|
createApplication(input: CreateApplicationInput!): Application!
|
||
|
|
createComponent(input: CreateComponentInput!): Component!
|
||
|
|
createGitRepo(input: CreateGitRepoInput!): GitRepo!
|
||
|
|
configureBranchProtection(repoId: ID!, branch: String!, protection: BranchProtectionInput!): BranchProtection!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Content Hierarchy
|
||
|
|
POST /api/v1/content/enterprises
|
||
|
|
GET /api/v1/content/enterprises
|
||
|
|
GET /api/v1/content/enterprises/{id}
|
||
|
|
|
||
|
|
POST /api/v1/content/portfolios
|
||
|
|
GET /api/v1/content/portfolios/{id}
|
||
|
|
|
||
|
|
POST /api/v1/content/products
|
||
|
|
GET /api/v1/content/products/{id}
|
||
|
|
|
||
|
|
POST /api/v1/content/applications
|
||
|
|
GET /api/v1/content/applications/{id}
|
||
|
|
|
||
|
|
POST /api/v1/content/components
|
||
|
|
GET /api/v1/content/components/{id}
|
||
|
|
|
||
|
|
# Git Integration
|
||
|
|
POST /api/v1/content/git-repos
|
||
|
|
GET /api/v1/content/git-repos
|
||
|
|
GET /api/v1/content/git-repos/{id}
|
||
|
|
POST /api/v1/content/git-repos/{id}/branch-protection
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Queries
|
||
|
|
|
||
|
|
#### Create Application
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createApplication(input: {
|
||
|
|
name: "web-application"
|
||
|
|
productId: "product-id"
|
||
|
|
ownership: {
|
||
|
|
owner: "dev-team"
|
||
|
|
team: "engineering"
|
||
|
|
contact: "dev-team@agency.gov"
|
||
|
|
}
|
||
|
|
governance: {
|
||
|
|
approvalWorkflows: [{
|
||
|
|
name: "code-review"
|
||
|
|
steps: [{
|
||
|
|
approver: "tech-lead"
|
||
|
|
role: "TECH_LEAD"
|
||
|
|
}]
|
||
|
|
required: true
|
||
|
|
}]
|
||
|
|
complianceTags: ["HIPAA", "SOC_2"]
|
||
|
|
}
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
product {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Create Git Repository
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createGitRepo(input: {
|
||
|
|
name: "web-application-repo"
|
||
|
|
applicationId: "application-id"
|
||
|
|
url: "https://git.sankofa.nexus/web-application"
|
||
|
|
branchStrategy: {
|
||
|
|
main: "main"
|
||
|
|
develop: "develop"
|
||
|
|
protectedBranches: ["main", "release/*"]
|
||
|
|
}
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
url
|
||
|
|
application {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## VI. Authentication and Authorization
|
||
|
|
|
||
|
|
### Authentication
|
||
|
|
|
||
|
|
All APIs use Keycloak for authentication:
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# Authentication via Keycloak
|
||
|
|
# Token obtained from Keycloak OAuth2/OIDC flow
|
||
|
|
# Include in Authorization header: Bearer <token>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Authorization
|
||
|
|
|
||
|
|
Authorization is enforced via RBAC:
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
# RBAC roles per plane:
|
||
|
|
# Commercial: Finance Admin, Billing Viewer, Cost Center Owner
|
||
|
|
# Tenancy: Tenant Owner, Security Admin, Identity Admin, Compliance Officer
|
||
|
|
# Subscription: Subscription Owner, Platform Admin, Service Operator, Auditor
|
||
|
|
# Environment: Environment Owner, Release Manager, Operator, Observer
|
||
|
|
# Content: Enterprise Architect, Portfolio Lead, Product Owner, Dev Lead, Contributor, Reviewer, Release Approver
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example: Authenticated Request
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST https://api.phoenix.sankofa.nexus/graphql \
|
||
|
|
-H "Authorization: Bearer <keycloak-token>" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"query": "query { tenant(id: \"tenant-id\") { id name } }"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## VII. Error Handling
|
||
|
|
|
||
|
|
### Error Response Format
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"errors": [
|
||
|
|
{
|
||
|
|
"message": "Error message",
|
||
|
|
"extensions": {
|
||
|
|
"code": "ERROR_CODE",
|
||
|
|
"field": "fieldName",
|
||
|
|
"resource": "ResourceType",
|
||
|
|
"resourceId": "resource-id"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"data": null
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Common Error Codes
|
||
|
|
|
||
|
|
- `UNAUTHORIZED` - Authentication required
|
||
|
|
- `FORBIDDEN` - Insufficient permissions
|
||
|
|
- `NOT_FOUND` - Resource not found
|
||
|
|
- `VALIDATION_ERROR` - Input validation failed
|
||
|
|
- `QUOTA_EXCEEDED` - Quota limit exceeded
|
||
|
|
- `POLICY_VIOLATION` - Policy violation
|
||
|
|
- `CONFLICT` - Resource conflict
|
||
|
|
- `INTERNAL_ERROR` - Internal server error
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## VIII. Rate Limiting
|
||
|
|
|
||
|
|
### Rate Limits
|
||
|
|
|
||
|
|
- **GraphQL**: 1000 requests per minute per tenant
|
||
|
|
- **REST**: 500 requests per minute per tenant
|
||
|
|
- **Billing Queries**: 100 requests per minute per client
|
||
|
|
- **Promotion Operations**: 50 requests per minute per environment
|
||
|
|
|
||
|
|
### Rate Limit Headers
|
||
|
|
|
||
|
|
```
|
||
|
|
X-RateLimit-Limit: 1000
|
||
|
|
X-RateLimit-Remaining: 950
|
||
|
|
X-RateLimit-Reset: 1640995200
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## IX. Pagination
|
||
|
|
|
||
|
|
### GraphQL Pagination
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
type Query {
|
||
|
|
tenants(
|
||
|
|
filter: TenantFilter
|
||
|
|
pagination: PaginationInput
|
||
|
|
): TenantConnection!
|
||
|
|
}
|
||
|
|
|
||
|
|
type TenantConnection {
|
||
|
|
edges: [TenantEdge!]!
|
||
|
|
pageInfo: PageInfo!
|
||
|
|
totalCount: Int!
|
||
|
|
}
|
||
|
|
|
||
|
|
type TenantEdge {
|
||
|
|
node: Tenant!
|
||
|
|
cursor: String!
|
||
|
|
}
|
||
|
|
|
||
|
|
type PageInfo {
|
||
|
|
hasNextPage: Boolean!
|
||
|
|
hasPreviousPage: Boolean!
|
||
|
|
startCursor: String
|
||
|
|
endCursor: String
|
||
|
|
}
|
||
|
|
|
||
|
|
input PaginationInput {
|
||
|
|
first: Int
|
||
|
|
after: String
|
||
|
|
last: Int
|
||
|
|
before: String
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Pagination
|
||
|
|
|
||
|
|
```
|
||
|
|
GET /api/v1/tenancy/tenants?page=1&pageSize=50
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": [...],
|
||
|
|
"pagination": {
|
||
|
|
"page": 1,
|
||
|
|
"pageSize": 50,
|
||
|
|
"totalPages": 10,
|
||
|
|
"totalCount": 500,
|
||
|
|
"hasNext": true,
|
||
|
|
"hasPrevious": false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## X. Webhooks and Subscriptions
|
||
|
|
|
||
|
|
### GraphQL Subscriptions
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
type Subscription {
|
||
|
|
clientUpdated(clientId: ID!): Client!
|
||
|
|
tenantUpdated(tenantId: ID!): Tenant!
|
||
|
|
subscriptionUpdated(subscriptionId: ID!): Subscription!
|
||
|
|
environmentUpdated(environmentId: ID!): Environment!
|
||
|
|
promotionStatusChanged(promotionId: ID!): PromotionResult!
|
||
|
|
quotaExceeded(subscriptionId: ID!): QuotaAlert!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### REST Webhooks
|
||
|
|
|
||
|
|
```
|
||
|
|
POST /api/v1/webhooks
|
||
|
|
GET /api/v1/webhooks
|
||
|
|
PUT /api/v1/webhooks/{id}
|
||
|
|
DELETE /api/v1/webhooks/{id}
|
||
|
|
```
|
||
|
|
|
||
|
|
Webhook Events:
|
||
|
|
- `client.created`
|
||
|
|
- `client.updated`
|
||
|
|
- `tenant.created`
|
||
|
|
- `tenant.updated`
|
||
|
|
- `subscription.created`
|
||
|
|
- `subscription.updated`
|
||
|
|
- `environment.created`
|
||
|
|
- `environment.updated`
|
||
|
|
- `promotion.started`
|
||
|
|
- `promotion.approved`
|
||
|
|
- `promotion.rejected`
|
||
|
|
- `quota.exceeded`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## XI. Multi-Region Support
|
||
|
|
|
||
|
|
### Regional API Endpoints
|
||
|
|
|
||
|
|
```
|
||
|
|
# Primary region
|
||
|
|
https://api.phoenix.sankofa.nexus
|
||
|
|
|
||
|
|
# Regional endpoints
|
||
|
|
https://api-region-a.phoenix.sankofa.nexus
|
||
|
|
https://api-region-b.phoenix.sankofa.nexus
|
||
|
|
https://api-region-c.phoenix.sankofa.nexus
|
||
|
|
```
|
||
|
|
|
||
|
|
### Cross-Region Operations
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
createCrossRegionSubscription(input: {
|
||
|
|
name: "multi-region-sub"
|
||
|
|
tenantId: "tenant-id"
|
||
|
|
regions: ["region-a", "region-b"]
|
||
|
|
regionalQuotas: {
|
||
|
|
"region-a": {
|
||
|
|
compute: { vcpu: 50, memory: 256 }
|
||
|
|
}
|
||
|
|
"region-b": {
|
||
|
|
compute: { vcpu: 50, memory: 256 }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}) {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
regions {
|
||
|
|
id
|
||
|
|
name
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- **[Operating Model](./OPERATING_MODEL.md)** - Complete operating model documentation
|
||
|
|
- **[MVP Control Plane](./MVP_CONTROL_PLANE.md)** - MVP API requirements
|
||
|
|
- **[Architecture Diagrams](./OPERATING_MODEL_DIAGRAMS.md)** - API integration diagrams
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Last Updated**: 2025-01-09
|
||
|
|
**Version**: 1.0
|
||
|
|
**Status**: Complete API Specification
|
||
|
|
|