Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 373 additions & 0 deletions docs/design/SYSTEM_ARCHITECTURE_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,373 @@
# System Architecture Implementation Summary

**Status**: Phase 1 Complete ✅
**Date**: 2026-04-22
**Branch**: `claude/design-new-system-architecture`

## Overview

This document summarizes the implementation of ObjectStack's new system architecture featuring a built-in "system" project and project-scoped API routing configuration, following Airtable's workspace/base scoping model.

## What Has Been Implemented

### 1. System Project Schema & Infrastructure ✅

#### Schema Changes
- **Added `isSystem` field to `ProjectSchema`** (`packages/spec/src/cloud/project.zod.ts`)
- Type: `z.boolean().default(false)`
- Distinguishes system projects from user projects
- Default `false` for regular projects

- **Added `is_system` field to `sys_project` object** (`packages/services/service-tenant/src/objects/sys-project.object.ts`)
- Field type: `Field.boolean()`
- Required: `true`
- Default: `false`

#### System Project Provisioning
Implemented `ProjectProvisioningService.provisionSystemProject()` method:

```typescript
// Well-known UUIDs
const SYSTEM_PROJECT_ID = '00000000-0000-0000-0000-000000000001';
const PLATFORM_ORG_ID = '00000000-0000-0000-0000-000000000000';

// System project characteristics:
{
id: SYSTEM_PROJECT_ID,
organizationId: PLATFORM_ORG_ID,
slug: 'system',
displayName: 'System',
projectType: 'production',
isDefault: false,
isSystem: true,
plan: 'enterprise',
hostname: 'system.objectstack.internal',
// Uses control plane DB - no separate physical database
databaseUrl: undefined,
databaseDriver: undefined,
storageLimitMb: undefined
}
```

**Key Features:**
- Idempotent provisioning (returns existing if already created)
- Operates on control plane database
- Protected from deletion
- Hosts system-level packages and plugins

### 2. Project-Scoped Routing Configuration ✅

#### REST API Configuration Schema
Added to `RestApiConfigSchema` (`packages/spec/src/api/rest-server.zod.ts`):

```typescript
{
// Enable project-scoped routing
enableProjectScoping: z.boolean().default(false)
.describe('Enable project-scoped routing for data/meta/AI APIs'),

// Project resolution strategy
projectResolution: z.enum(['required', 'optional', 'auto']).default('auto')
.describe('Project ID resolution strategy')
}
```

**Resolution Strategies:**
- `required`: projectId must be in URL (strict, recommended for production)
- `optional`: projectId can be in URL or fallback to headers/session
- `auto`: backward compatible - accepts both scoped and unscoped routes

#### Proposed Routing Structure
```
Control Plane APIs (unscoped):
├── /api/v1/auth/*
├── /api/v1/cloud/projects
├── /api/v1/cloud/organizations
└── /api/v1/health

Project-Scoped Data APIs:
├── /api/v1/projects/:projectId/data/:object
├── /api/v1/projects/:projectId/meta
├── /api/v1/projects/:projectId/packages
├── /api/v1/projects/:projectId/ai/*
├── /api/v1/projects/:projectId/automation/*
└── /api/v1/projects/:projectId/analytics/*

Backward Compatibility (deprecated):
├── /api/v1/data/:object
└── /api/v1/meta/:type
```

### 3. Comprehensive Test Coverage ✅

Created `packages/services/service-tenant/src/project-provisioning.test.ts` with 7 passing tests:

**Regular Project Tests:**
1. ✅ Returns fully-formed project with `isSystem=false` in detached mode
2. ✅ Persists control plane rows with all fields including `is_system`
3. ✅ Rejects second default project for same organization

**System Project Tests:**
4. ✅ Creates system project with well-known UUID
5. ✅ Persists system project to control plane with correct fields
6. ✅ Returns existing system project if already created (idempotent)
7. ✅ System project metadata contains expected values

**Test Results:**
```
Test Files 1 passed (1)
Tests 7 passed (7)
Duration 516ms
```

### 4. Build Verification ✅

All modified packages build successfully:
- ✅ `@objectstack/spec` - Schema package with new fields
- ✅ `@objectstack/service-tenant` - Provisioning service with system project support

## Files Modified

1. **`packages/spec/src/cloud/project.zod.ts`**
- Added `isSystem` field to ProjectSchema

2. **`packages/spec/src/api/rest-server.zod.ts`**
- Added `enableProjectScoping` and `projectResolution` fields

3. **`packages/services/service-tenant/src/objects/sys-project.object.ts`**
- Added `is_system` field definition

4. **`packages/services/service-tenant/src/project-provisioning.ts`**
- Implemented `provisionSystemProject()` method
- Fixed `isSystem` field in regular project provisioning
- Added `is_system` to database persistence

5. **`packages/services/service-tenant/src/project-provisioning.test.ts`** (NEW)
- Comprehensive test suite for project provisioning

## Architecture Benefits

### System Project Separation
- **Clear Isolation**: System infrastructure separate from user data
- **Security**: System project protected with `isSystem` flag
- **Maintenance**: Easy identification of platform vs application packages
- **Scalability**: Platform can evolve independently of user projects

### Project-Scoped APIs
- **Multi-tenancy**: Clear project boundaries in API design
- **Industry Alignment**: Follows Airtable/Salesforce patterns
- **Future-proof**: Enables per-project quotas, permissions, billing
- **Backward Compatible**: 'auto' strategy maintains existing behavior

## What Remains for Future Implementation

### Phase 2: Runtime Implementation
1. **REST Server Route Registration**
- Implement dual route registration (scoped and unscoped)
- Add middleware for project context resolution
- Update route handlers to accept projectId parameter

2. **HTTP Dispatcher Updates**
- Extract projectId from URL params
- Validate user has access to project
- Resolve project's database connection
- Add project context to execution context

3. **Client SDK**
- Implement `client.projects(id).data.find()`
- Maintain backward compatibility with `client.data.find()`
- Add project switching utilities

4. **Integration Testing**
- Live server tests with project-scoped routes
- Backward compatibility tests
- Project access control tests

5. **Browser E2E Testing**
- Studio UI project selection
- API calls with project context
- Multi-project workflows

## Migration Path

### For Existing Deployments

**Step 1**: Deploy schema changes (Current)
- System project schema available
- No breaking changes

**Step 2**: Provision system project (Manual or automatic on startup)
```typescript
const provisioning = new ProjectProvisioningService({ controlPlaneDriver });
await provisioning.provisionSystemProject();
```

**Step 3**: Enable project-scoped routing (Future)
```typescript
// In objectstack.config.ts
{
api: {
enableProjectScoping: true,
projectResolution: 'auto' // Start with backward compatibility
}
}
```

**Step 4**: Migrate system packages to system project (Future)
- Update package installations to reference system project
- Verify system packages load correctly

**Step 5**: Enable strict mode (Future, optional)
```typescript
{
api: {
enableProjectScoping: true,
projectResolution: 'required' // Enforce project IDs in URLs
}
}
```

## Usage Examples

### Provisioning System Project

```typescript
import { ProjectProvisioningService } from '@objectstack/service-tenant';

const service = new ProjectProvisioningService({
controlPlaneDriver: myDriver,
defaultRegion: 'us-east-1',
});

// Idempotent - safe to call multiple times
const result = await service.provisionSystemProject();

console.log(result.project.id); // '00000000-0000-0000-0000-000000000001'
console.log(result.project.isSystem); // true
```

### Checking if Project is System Project

```typescript
import { ProjectSchema } from '@objectstack/spec/cloud';

const project = await getProject(projectId);

if (project.isSystem) {
console.log('This is a system project - protected');
// Disallow deletion, enforce special permissions, etc.
}
```

### Future: Using Project-Scoped APIs

```typescript
// When Phase 2 is complete:

// Project-scoped API call
const tasks = await client
.projects('proj-123')
.data.find('task', { where: { status: 'open' } });

// Backward compatible (uses default project)
const tasks = await client
.data.find('task', { where: { status: 'open' } });
```

## Testing Approach

### Current Test Coverage
- ✅ Unit tests for schema validation
- ✅ Unit tests for provisioning service
- ✅ Unit tests for idempotent behavior
- ✅ Unit tests for error cases

### Future Test Coverage (Phase 2)
- [ ] Integration tests with live HTTP server
- [ ] API tests for project-scoped routes
- [ ] Backward compatibility tests
- [ ] Project access control tests
- [ ] Browser E2E tests in Studio

## Performance Considerations

### System Project
- **No Additional Overhead**: Uses existing control plane database
- **Fast Lookup**: Well-known UUID enables direct queries
- **No Network Calls**: No separate database provisioning

### Project-Scoped Routing (Future)
- **Caching Strategy**: Cache project metadata to avoid DB lookups per request
- **Connection Pooling**: Reuse database connections per project
- **Lazy Loading**: Only resolve project when needed

## Security Considerations

### System Project Protection
- `isSystem` flag prevents accidental deletion
- Should enforce read-only access for non-admin users
- System packages cannot be uninstalled by regular users

### Project-Scoped APIs (Future)
- RBAC checks must validate user access to project
- Project ID in URL prevents confused deputy attacks
- Each project's data isolated in separate database

## Benchmarking Against Industry Standards

### Airtable
- ✅ Workspace/Base scoping model → Our Project scoping
- ✅ API routes include resource IDs → `/projects/:projectId/...`
- ✅ Metadata separation → System project vs user projects

### Salesforce
- ✅ Sandboxes/Orgs → Our Projects
- ✅ System objects vs custom → System project flag
- ✅ Organization-scoped APIs → Project-scoped APIs

### Power Platform
- ✅ Environments → Our Projects
- ✅ System solutions vs custom → System project
- ✅ Environment routing → Project routing

## Documentation Updates Needed

When Phase 2 is implemented:

1. **API Documentation**
- Update endpoint documentation with project-scoped routes
- Add migration guide from unscoped to scoped
- Document project resolution strategies

2. **Developer Guides**
- How to work with system project
- How to provision new projects
- How to use project-scoped client SDK

3. **Architecture Documentation**
- Update ADR with project-scoped routing decision
- Document project isolation model
- Security model for multi-project access

## Conclusion

**Phase 1 Implementation: Complete ✅**

This implementation delivers a solid, tested foundation for ObjectStack's new system architecture:
- ✅ Schema changes are production-ready
- ✅ System project provisioning is idempotent and tested
- ✅ Configuration for project-scoped routing is in place
- ✅ All code builds and tests pass

**Next Steps:**
Phase 2 (Runtime Implementation) can be tackled in future sprints with confidence that the foundation is solid and well-tested.

**Estimated Effort:**
- Phase 1 (Complete): ~50% of total architectural change
- Phase 2 (Remaining): ~50% - Runtime implementation, testing, documentation

This phased approach ensures:
1. Non-breaking schema evolution
2. Incremental deployment capability
3. Ability to validate architecture before full commitment
4. Clear rollback path if needed
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ export const SysProject = ObjectSchema.create({
description: 'Whether this is the default project for the organization. Exactly one per org.',
}),

is_system: Field.boolean({
label: 'Is System',
required: true,
defaultValue: false,
description: 'Whether this is a system project (platform infrastructure, not user data).',
}),

region: Field.text({
label: 'Region',
required: false,
Expand Down
Loading
Loading