Version 3.2
IG is a multi-tenant AI media generation control plane.
It provides a provider-neutral API for submitting media-generation requests, executing them asynchronously, resolving reference media, storing generated and uploaded artifacts, and managing tenant-specific provider configuration.
V3 currently implements image generation. The media interface is intentionally designed so that future video and audio capabilities can use the same control-plane concepts without exposing provider-specific implementation details to higher-level applications.
IG is also intended to serve as infrastructure for higher-level applications, such as Sandcastle and Sociosight.
IG V3 includes:
- Multi-tenant backend architecture
- Tenant resolution by hostname
- Tenant membership and administrative roles
- Tenant-specific authentication policy
- Password and Google authentication support
- Tenant-specific model-provider configuration
- Encrypted provider credentials
- Provider-neutral media generation API
- OpenAI image provider adapter
- Semantic image styles
- Image quality and aspect-ratio preferences
- Single-image and panel-grid layouts
- Reference images with semantic roles and strengths
- Artifact upload
- Manual/external artifact import
- Persistent generation history
- Asynchronous jobs and executions
- Vercel Queue
- Redis/RQ as an optional queue backend
- Vercel Blob artifact storage
- Local artifact-storage implementation
- PostgreSQL persistence
- Alembic migrations
- React/Vite frontend
- FastAPI backend
- GitHub → Vercel CI/CD deployment
The standard V3 deployment is:
Browser / API Client / 3rd Party Apps
│
▼
Vercel Frontend
React / Vite
│
▼
FastAPI API
│
┌───────┼──────────────┐
│ │ │
▼ ▼ ▼
Neon Vercel Queue Tenant Provider
PostgreSQL Configuration
│
▼
Provider Adapter
│
▼
OpenAI
│
▼
Generated Media
│
▼
Vercel Blob
│
▼
Artifact
The development machine is not part of the deployed runtime.
Normal development consists of editing, testing, committing, and pushing the repository. Vercel CI/CD deploys changes from GitHub.
IG separates provider- or infrastructure-specific behavior from reusable control-plane primitives.
A useful design question is:
Is this provider/infrastructure-specific, or is this a general control-plane primitive?
Reusable control-plane concepts include:
Tenant
Provider
MediaType
Capability
Job
Execution
Artifact
Policy
Usage
Provider-specific behavior belongs inside provider adapters.
Storage-specific behavior belongs behind the artifact-storage abstraction.
Queue-specific behavior belongs behind the job-queue abstraction.
Higher-level applications such as Sandcastle and Sociosight should communicate with IG through semantic control-plane interfaces rather than directly through model-provider APIs.
The primary generation interface is provider-neutral.
A media request may contain concepts such as:
media_type
intent
quality
style
aspect_ratio
layout
references
strategy
interface_level
advanced
Example image-generation request:
{
"media_type": "image",
"interface_level": "easy",
"intent": {
"description": "A character walking through a rainy city street at night."
},
"output": {
"quality": "standard",
"aspect_ratio": "2:3",
"style": "photoreal",
"count": 1,
"layout": {
"type": "single"
}
},
"references": []
}Generation endpoint:
POST /v2/media/generations
IG resolves the semantic request internally:
MediaGenerationRequest
↓
semantic validation
↓
tenant resolution
↓
reference resolution
↓
provider configuration
↓
provider adapter
↓
model provider
Higher-level clients therefore do not need to know:
- OpenAI image sizes
- provider-specific API methods
- provider credential formats
- artifact storage implementation
- queue implementation
Image style is represented as a provider-neutral semantic preference.
Examples include:
auto
photoreal
anime
3d
painterly
illustration
comic
cinematic
Provider adapters decide how each semantic style is represented to the underlying provider.
This allows a request such as:
{
"style": "anime"
}to remain independent of any particular provider API.
Generation requests may reference existing artifacts.
Example:
{
"artifact_id": "<artifact-id>",
"role": "character",
"strength": "strong"
}Supported reference roles currently include:
character
outfit
style
pose
composition
environment
previous_output
Reference strength may be:
loose
normal
strong
IG resolves the artifact, validates tenant/user access, retrieves its binary content from the configured artifact store, and passes the reference to the selected provider adapter.
Reference roles are request-specific.
An artifact itself is not permanently classified as a character, style, or previous output.
The same artifact may therefore be used as:
character in request A
style in request B
previous_output in request C
Images can be uploaded as artifacts independently of generation.
Supported upload formats:
PNG
JPEG
WebP
Upload endpoint:
POST /v2/artifacts/uploads
Uploaded artifacts may represent:
- reference material
- externally generated images
- manually produced images
- previous outputs imported into IG
An uploaded artifact may use origins such as:
reference
external_generation
Artifact origin describes where the artifact came from.
Reference role describes how a later generation uses it.
These are intentionally separate concepts.
Artifact APIs include:
GET /v2/artifacts
GET /v2/artifacts/{artifact_id}
GET /v2/artifacts/{artifact_id}/content
POST /v2/artifacts/uploads
Storage implementation details are hidden from API consumers.
Clients work with artifact IDs and artifact-content endpoints rather than physical storage URLs.
IG uses provider adapters rather than calling model APIs directly from generation services.
Current image provider:
OpenAI
Current OpenAI default image model:
gpt-image-2
Model resolution follows:
explicit request model
↓
tenant provider default
↓
provider adapter default
The provider-neutral request is resolved before the provider adapter performs generation.
Future providers can implement the same adapter interface.
Model-provider credentials are not global application environment variables.
Each tenant has its own provider configuration.
Conceptually:
Tenant
│
└── Provider Configuration
├── provider
├── enabled
├── encrypted credentials
└── non-secret configuration
Credentials are represented as a generic key/value structure.
OpenAI currently requires:
{
"api_key": "..."
}A future provider may instead require:
{
"api_key": "...",
"api_token": "...",
"account_id": "..."
}or:
{
"client_id": "...",
"client_secret": "...",
"region": "..."
}This prevents IG's database schema from being designed around the authentication requirements of any single provider.
Tenant provider credentials are encrypted before being stored in PostgreSQL.
The deployment requires:
TENANT_SECRET_ENCRYPTION_KEY
Generate a Fernet key with:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Store this value securely in the deployment environment.
Do not commit it to Git.
The encryption key is a platform infrastructure secret.
Individual provider credentials are tenant data and should be configured through the tenant provider administration API.
Tenant administrators can configure model providers.
List providers:
GET /v2/tenant/providers
Read one provider:
GET /v2/tenant/providers/{provider}
Create or update a provider:
PUT /v2/tenant/providers/{provider}
Example:
PUT /v2/tenant/providers/openai
{
"enabled": true,
"credentials": {
"api_key": "<openai-api-key>"
},
"config": {
"default_model": "gpt-image-2"
}
}Credentials are encrypted before persistence.
GET responses expose only safe metadata.
Example:
{
"provider": "openai",
"enabled": true,
"has_credentials": true,
"config": {
"default_model": "gpt-image-2"
}
}Provider credentials are never returned through these APIs.
IG V3 is tenant-aware throughout the backend.
An incoming request resolves approximately as:
hostname
↓
TenantDomain
↓
Tenant
↓
authenticated User
↓
TenantMembership
↓
TenantContext
Because hostname resolution occurs before most tenant-aware operations, every usable tenant requires at least one TenantDomain mapping.
Tenant-scoped resources include:
- memberships
- authentication configuration
- provider configuration
- jobs
- media-generation requests
- executions
- artifacts
Tenant authentication settings currently include:
allow_self_registration
allow_password_authentication
allow_google_authentication
Public self-registration may be enabled or disabled per tenant.
Password and Google authentication can also be enabled or disabled independently, subject to application validation.
Tenant administrators have access to tenant-management operations that ordinary members do not.
Public tenant authentication configuration:
GET /v2/tenant/auth-config
Administrative authentication-provider configuration:
GET /v2/tenant/auth-providers
PUT /v2/tenant/auth-providers/{provider}
Currently recognized authentication providers include:
google
entra
Example:
PUT /v2/tenant/auth-providers/google
{
"client_id": "<google-client-id>",
"issuer": null,
"metadata": {}
}Read tenant settings:
GET /v2/tenant/settings
Update tenant settings:
PATCH /v2/tenant/settings
Example:
{
"allow_self_registration": false,
"allow_password_authentication": true,
"allow_google_authentication": true
}Admin membership is required.
The current API updates an existing TenantSettings row.
It does not currently create the first TenantSettings row during installation bootstrap.
List members:
GET /v2/tenant/members
Update member role/status:
PATCH /v2/tenant/members/{user_id}
Example:
{
"role": "admin",
"status": "active"
}Supported roles:
admin
member
Supported statuses:
active
inactive
IG prevents removal or deactivation of the tenant's last active administrator.
Once the first administrator exists, invitations are the preferred way to add users when public registration is disabled.
Create invitation:
POST /v2/tenant/invitations
Example:
{
"email": "[email protected]",
"role": "member"
}Supported invitation roles:
member
admin
During V3 development, the returned invitation response includes the invitation token directly.
Future versions may deliver this token through email or another notification mechanism.
List invitations:
GET /v2/tenant/invitations
Revoke pending invitation:
DELETE /v2/tenant/invitations/{invitation_id}
Existing authenticated user accepts invitation:
POST /v2/tenant/invitations/accept
{
"token": "<invitation-token>"
}New user registers from invitation:
POST /v2/tenant/invitations/register
{
"token": "<invitation-token>",
"password": "<password>",
"name": "Example User"
}This flow creates:
User
TenantMembership
Session
When enabled by tenant policy:
POST /v2/auth/register
Example:
{
"email": "[email protected]",
"password": "<password>",
"name": "Example User"
}This creates:
User
TenantMembership(role=member)
for the tenant resolved from the request hostname.
Self-registration requires:
TenantDomain resolves successfully
TenantSettings exists
allow_password_authentication = true
allow_self_registration = true
Therefore self-registration cannot bootstrap an entirely empty IG installation.
Authenticated clients can inspect their current tenant context:
GET /v2/tenant
Example response:
{
"tenant_id": "...",
"slug": "example",
"name": "Example Tenant",
"status": "active",
"membership": {
"role": "admin",
"status": "active"
}
}This is useful for verifying:
hostname
→ TenantDomain
→ Tenant
→ TenantMembership
resolution.
The supported primary deployment architecture is:
Vercel
React / Vite frontend
FastAPI backend
Vercel Queue
Vercel Blob
Neon
PostgreSQL
Model providers
OpenAI
Redis/RQ remains available as an alternative queue implementation, but it is not required for the normal Vercel deployment.
The historical Docker deployment remains in the repository for reference but is not treated as the primary or fully validated V3 deployment path.
git clone https://github.com/<your-account>/ig.git
cd igUpstream repository:
github.com/ywptr/ig
Import the GitHub repository into Vercel.
IG contains Vercel project configuration for the React/Vite frontend and FastAPI backend.
GitHub pushes may be used as the normal CI/CD workflow.
Typical routing includes:
/health → FastAPI
/v1/* → FastAPI
/v2/* → FastAPI
/* → frontend
The recommended database is Neon PostgreSQL.
Configure:
DATABASE_URL
Example:
postgresql+psycopg://user:password@host/database?sslmode=require
The standard deployment uses Vercel Blob.
Configure:
ARTIFACT_STORE_PROVIDER=vercel
The required Vercel Blob credentials should be supplied by the Vercel project/runtime.
The standard V3 queue is:
JOB_QUEUE_PROVIDER=vercel
This does not require Redis or a separately hosted RQ worker.
Generate a Fernet key:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Configure the resulting value as:
TENANT_SECRET_ENCRYPTION_KEY
in the deployment environment.
Do not commit the key.
Typical V3 production configuration:
DATABASE_URL=<postgresql-url>
TENANT_SECRET_ENCRYPTION_KEY=<fernet-key>
JOB_QUEUE_PROVIDER=vercel
ARTIFACT_STORE_PROVIDER=vercel
LOG_LEVEL=INFO
IG_TRACE_JOBS=false
Provider API credentials such as OpenAI API keys do not belong in the normal deployment environment.
They are configured per tenant after tenant initialization.
A normal Linux development environment requires:
Git
Python
Node.js / npm
Vercel CLI
Create a Python environment:
python -m venv .venv
source .venv/bin/activateInstall backend dependencies:
pip install -e .Install frontend dependencies:
cd frontend
npm install
cd ..Ensure DATABASE_URL points to the intended database.
Run:
alembic upgrade headVerify:
alembic currentA fresh database must be migrated before IG can be initialized.
The normal V3 workflow uses GitHub-connected Vercel CI/CD.
git add .
git commit -m "..."
git pushVercel automatically builds and deploys the connected repository.
Manual Vercel deployment may also be used when necessary.
A completely fresh IG installation currently requires an administrative database bootstrap before normal tenant APIs can be used.
This is necessary because the tenant itself is resolved from the request hostname.
The required bootstrap order is:
1. Tenant
2. TenantDomain
3. User
4. TenantMembership with role=admin
5. TenantSettings
The first TenantDomain.hostname must match the hostname through which the deployment is accessed.
For example, if the deployment is accessed through:
ig.example.com
then a corresponding tenant-domain record must exist for:
ig.example.com
before tenant-aware APIs can resolve the tenant.
The following first-installation capabilities are not currently exposed as complete bootstrap APIs:
| Required object | Creation API available for initial bootstrap? | Notes |
|---|---|---|
Tenant |
No | Must currently be created administratively |
TenantDomain |
No | Must exist before hostname-based tenant resolution works |
Initial admin User |
No complete bootstrap API | Normal registration requires an already resolved/configured tenant |
Initial admin TenantMembership |
No complete bootstrap API | Invitations/member APIs require an existing admin |
Initial TenantSettings |
No | Existing settings API updates an existing row |
Therefore the very first tenant requires one administrative database bootstrap operation.
Once these objects exist, normal tenant administration can proceed through application APIs.
After bootstrapping the first tenant and admin, authenticate and call:
GET /v2/tenant
This confirms:
hostname
→ TenantDomain
→ Tenant
→ User
→ TenantMembership
resolution.
Once the tenant administrator can authenticate, configure OpenAI:
PUT /v2/tenant/providers/openai
{
"enabled": true,
"credentials": {
"api_key": "<openai-api-key>"
},
"config": {
"default_model": "gpt-image-2"
}
}The credential is encrypted before being written to PostgreSQL.
A global OPENAI_API_KEY environment variable is not required for the V3 runtime.
Open:
https://<production-domain>/health
A healthy deployment should return something similar to:
{
"status": "ok",
"queue_provider": "vercel"
}Then verify:
- tenant resolution
- login
- standard image generation
- semantic style
- artifact upload
- artifact retrieval
- image generation using an uploaded reference
- tenant provider configuration
- provider generation with no global provider API key configured
The repository includes .env.example.
Primary V3 variables include:
DATABASE_URL
TENANT_SECRET_ENCRYPTION_KEY
JOB_QUEUE_PROVIDER
ARTIFACT_STORE_PROVIDER
LOG_LEVEL
IG_TRACE_JOBS
Optional queue-development variables include:
REDIS_URL
IG_QUEUE_BASE_URL
Provider API credentials should not normally be placed in .env.
Vercel Queue is the standard V3 execution backend.
FastAPI
↓
Vercel Queue
↓
subscriber
↓
provider adapter
↓
artifact persistence
Use:
JOB_QUEUE_PROVIDER=vercel
Redis/RQ remains available as an optional queue implementation.
Use:
JOB_QUEUE_PROVIDER=rq
and configure:
REDIS_URL
An RQ deployment also requires a persistent RQ worker.
This execution path is retained as an alternative architecture but is not required for the standard V3 deployment.
Supported storage implementations currently include:
Vercel Blob
local filesystem
Use:
ARTIFACT_STORE_PROVIDER=vercel
for the standard cloud deployment.
Artifacts expose storage-independent IDs to API consumers.
Earlier IG versions used Docker Compose with:
Nginx
FastAPI
MariaDB
Redis / RQ
local artifact storage
Docker-related files remain in the repository for historical reference and experimentation.
The Docker deployment is not currently treated as the primary or fully validated V3 deployment path.
V3 development and deployment are centered on:
Vercel
Neon
Vercel Queue
Vercel Blob
No effort is currently being spent maintaining feature parity between the V3 cloud deployment and the legacy Docker architecture.
Do not commit:
DATABASE_URL
TENANT_SECRET_ENCRYPTION_KEY
provider credentials
session credentials
Blob credentials
Redis credentials
Tenant provider credentials are encrypted before database persistence.
Provider adapters must be instantiated using credentials belonging to the current tenant.
Provider instances containing tenant credentials must not be globally shared between tenants.
Artifact access is tenant-scoped.
Administrative tenant configuration requires tenant-admin membership.
Provider credential APIs never return stored secrets.
At least one active tenant administrator must be retained.
V3 establishes a cloud-hosted, multi-tenant media control plane.
Validated primary runtime:
Vercel
frontend
FastAPI
Queue
Blob
Neon
PostgreSQL
OpenAI
image generation
Implemented V3 capabilities include:
- cloud-native runtime
- multi-tenancy
- tenant-domain resolution
- tenant memberships
- tenant-specific authentication policy
- invitations
- Google authentication
- encrypted tenant provider credentials
- provider-neutral media-generation API
- semantic image styles
- reference-image generation
- artifact upload/import
- pluggable provider architecture
- pluggable storage
- pluggable queue execution
The development workstation is not part of the deployed runtime.
V3 intentionally stops short of implementing every possible control-plane feature. Its primary goals:
- Multitenant Ready
- Multiprovider Ready
- Capable of single and multipanel image generation
- Dynamic and future-proof media generation API
The following currently unexposed or partially exposed administrative capabilities should become first-class platform APIs and/or administrative UI in a future release:
- Create tenant
- Update tenant metadata/status
- Create tenant-domain mapping
- List tenant domains
- Update tenant-domain mapping
- Delete tenant-domain mapping
- Create the initial tenant administrator
- Create an initial tenant membership
- Create initial tenant settings
- Fully API-driven first-installation bootstrap
- Tenant lifecycle administration
- Tenant management UI
These should eventually remove the need for direct database manipulation during first deployment.
- Google Drive integration
- Google Drive as a tenant-selectable artifact backend
- User-owned Drive storage
- Additional cloud object-storage adapters
- Storage quotas and policies
- Artifact retention policies
- Tenant/shared artifact visibility rules
- Additional image provider adapters
- Gemini / Google model adapters
- Other hosted model APIs
- Self-hosted inference providers
- Tenant-provided model endpoints
- BYO model/inference infrastructure
- Video generation providers
- Audio generation providers
- Provider capability discovery
- Provider-specific health/status checks
- Provider fallback/routing policies
- Usage metering
- Provider-cost accounting
- Platform-managed provider credentials
- BYOK versus managed-provider modes
- Tenant billing
- Usage quotas
- Credit balances
- Plan enforcement
- Regional/commercial billing rules
- Richer tenant-administration UI
- Invitation delivery by email (partially completed; need to interface with a proper mail server)
- Additional identity providers and Enterprise SSO
- Domain-based onboarding
- Tenant-level identity policy
- Administrative audit logs
- Session/security management
- Tenant-shared artifacts
- Explicit artifact visibility:
- private
- tenant
- Reference libraries
- Named characters/styles/assets
- Artifact lineage
- Derived-artifact relationships
- Richer provenance
- Additional image formats where useful
- Video artifacts
- Audio artifacts
- Provider capability registry
- Model registry
- Capability routing
- Usage policies
- Quotas
- Execution tracing
- Observability
- Rate limiting
- Runtime policy enforcement
- Integration with authority/policy controls
Licensed under the Apache License 2.0.
Copyright © 2026 Yogi Wiputra
Repository:
github.com/ywptr/ig
See LICENSE for the complete license text.
User-provided prompts, uploaded content, and generated content remain the property of their respective users, subject to applicable provider terms, licenses, and law.
IG does not claim ownership of user-generated content.
Users are responsible for ensuring that their inputs and generated outputs comply with applicable law, provider policies, and third-party rights.
V3 — Multi-tenant cloud media control plane
The core V3 platform is operational. The frontend still requires work and currently is limited to current-tenant functions.