Domain · Specifications

Aja Domain — Technical Specifications

Aja consists of 39 library projects under libs/aja/ and 5 application projects under apps/aja/.

17sections34 minread

On this page

Aja — Motion AI and Animation Platform

This document specifies the implemented surface of the Aja domain: the domain objects and their fields, enums and union types, state values, HTTP API surface, in-process event model, persistence, validation rules, configuration, and acceptance criteria. Every type, field, enum value, endpoint, and event named here is traceable to source under libs/aja/* or apps/aja/*.

A note on naming: the motion-pipeline service, its SDK, and several libraries were authored under the @lilith/ / "Lilith Motion Pipeline" name and retain those internal labels in code comments and one package name (@lilith/svc-reference-video). The domain itself is Aja. Where the code uses @lilith/..., this document notes it.


1. Domain Inventory#

Aja consists of 39 library projects under libs/aja/ and 5 application projects under apps/aja/.

Applications (apps/aja/)#

The table below lists each application, its published package name, runtime framework, default port, and primary responsibility.

Project Package Runtime Default port Responsibility
svc-motion-pipeline @aja/svc-motion-pipeline Fastify 8090 Pipeline orchestration: configs, jobs, delivery
svc-motion-ai @aja/svc-motion-ai Hono 3040 Video-to-motion, retargeting, skeletal animation, embodied instruction
svc-reference-video @lilith/svc-reference-video library Reference video library: ingest, scene/metadata, search, annotation, collaboration
cli @aja/cli Commander Command-line interface (aja / aja-motion)
docs (unnamed) Markdown documentation site (README.md, api/, examples/, integration/, best-practices/)

svc-reference-video ships as a TypeScript library (main: dist/index.js, no HTTP server entry) — its module exports the reference-video subsystem.

Libraries (libs/aja/)#

All libraries publish under the @aja/ scope except the Python SDK. The 39 libraries listed below are alphabetically ordered for lookup.

animation-blending, asset-storage, avatar-integration, avatar-library, avatar-preview-ui, batch-inference, blend-shape-retargeting, bone-mapping-ui, consent-management, content-moderation, content-security, content-watermarking, data-retention, depth-sensing, distributed-workers, domain-motion-pipelines, film-pipeline, fitness-animation, human-mesh-recovery, model-optimization, motion-formats, motion-integration, motion-pipeline-sdk, motion-pipeline-sdk-python, motion-processing, motion-quality, motion-validation, multi-view-reconstruction, neural-retargeting, optimization-ik, pipeline-cache, pipeline-parallelism, pose-lifting, privacy-protection, proportional-adaptation, result-aggregation, semantic-retargeting, skeleton-mapping, video-chunking.

motion-pipeline-sdk-python has no package.json name; its Python distribution (pyproject.toml) is aja-motion-pipeline and its import package is aja_motion_pipeline.


2. Core Motion Data Models (@aja/motion-formats)#

@aja/motion-formats is the foundation library: it defines the canonical motion data model used across all 39 Aja libraries and the per-format file structures for import and export. No other Aja library defines its own motion primitives — they all re-export or depend on this one. Source: libs/aja/motion-formats/src/types.ts.

2.1 Math and Color Primitives#

The math primitives below are used everywhere joint positions and rotations are stored. All rotation representations are provided so callers can work in whatever convention their rendering pipeline expects.

Type Shape
Point3D { x, y, z: number }
Point2D { x, y: number }
Quaternion { x, y, z, w: number }
EulerAngles { x, y, z: number; order: EulerOrder }
AxisAngle { axis: Point3D; angle: number }
Matrix4x4 16-number tuple, row-major
Matrix3x3 9-number tuple, row-major
Color3 { r, g, b: number }
Color4 { r, g, b, a: number }

EulerOrder is one of 'XYZ' | 'XZY' | 'YXZ' | 'YZX' | 'ZXY' | 'ZYX'.

2.2 SkeletonJoint and SkeletonDefinition#

A skeleton is a tree of joints. SkeletonJoint represents one node in that tree — its position, orientation, scale, and its connection to its parent and children via index references.

SkeletonJoint fields:

Field Type Notes
name string required
index number array index
parentIndex number -1 for root
localPosition Point3D required
localRotation Quaternion required
localScale Point3D required
worldPosition Point3D optional
worldRotation Quaternion optional
children number[] child joint indices

SkeletonDefinition wraps the joint array with top-level metadata: name, joints: SkeletonJoint[], rootIndex, optional restPose: AnimationFrame, optional metadata.

2.3 Animation Model#

The animation model builds from individual keyframe values up to a full clip. Understanding this hierarchy is important before reading the type definitions.

KeyframeInterpolation describes how values are interpolated between keyframes: 'linear' | 'step' | 'cubic' | 'hermite' | 'bezier'.

AnimationProperty identifies which aspect of a joint a curve animates — one of position.{x,y,z}, rotation.{x,y,z,w}, euler.{x,y,z}, scale.{x,y,z}, blendShape.

Keyframe<T> is a single timed sample: time, value: T, interpolation: KeyframeInterpolation, optional inTangent/outTangent.

AnimationCurve<T> is the full set of keyframes for one property on one joint: jointName, property: AnimationProperty, keyframes: Keyframe<T>[].

JointTransform captures the transform state of a single joint at one moment: jointIndex, jointName, localPosition, localRotation, localScale, optional worldPosition/worldRotation.

AnimationFrame is the full skeleton state at one point in time: time, frameIndex, jointTransforms: JointTransform[], optional rootPosition, rootRotation, blendShapes: Map<string, number>.

AnimationClip is the central motion object — a complete recorded or generated animation sequence. Its fields are:

Field Type Required
id string yes
name string yes
skeleton SkeletonDefinition yes
frames AnimationFrame[] yes
duration number (seconds) yes
frameRate number yes
frameCount number yes
loopable boolean yes
curves AnimationCurve<number>[] optional
events AnimationEvent[] optional
metadata AnimationMetadata optional

AnimationEvent marks a named moment in the timeline: time, name, optional data.

AnimationMetadata carries provenance and categorization for the clip: source, optional captureDate, performer, action, category: MotionCategory, tags, quality: QualityMetrics, customData.

MotionCategory classifies the type of motion: 'locomotion' | 'combat' | 'dance' | 'sports' | 'gesture' | 'expression' | 'interaction' | 'idle' | 'transition' | 'other'.

QualityMetrics carries the six scoring fields inline with the clip: jitterScore (0–1, lower better), footSlidingDistance (meters), boneLengthVariance (0–1, lower better), smoothnessScore (0–1, higher better), physicalPlausibility (0–1, higher better), overallScore (0–1, higher better).

2.4 Standard Skeleton Types#

StandardSkeletonType is the union of all skeleton conventions that Aja knows about out of the box: 'mixamo' | 'unity-humanoid' | 'unreal-mannequin' | 'maya-hik' | 'motionbuilder' | 'cmu' | 'h36m' | 'coco' | 'openpose' | 'mediapipe-pose' | 'smpl' | 'smplx' | 'custom'.

Two exported constants drive all cross-skeleton work and must stay index-aligned — that is, index i in STANDARD_JOINT_NAMES[type] must correspond to index i in SKELETON_PARENT_INDICES[type]:

  • STANDARD_JOINT_NAMES: Record<StandardSkeletonType, string[]> — the canonical joint name list per skeleton (e.g., 65 names for mixamo, 22 for unity-humanoid, 22 for unreal-mannequin, 17 for h36m, 17 for coco, 34 for mediapipe-pose, 24 for smpl, 55 for smplx).
  • SKELETON_PARENT_INDICES: Record<StandardSkeletonType, number[]> — parent joint index per joint for the same skeletons.

2.5 Supported Motion Formats#

MotionFormat is the union of all format identifiers: 'bvh' | 'fbx' | 'gltf' | 'glb' | 'usd' | 'usda' | 'usdc' | 'usdz' | 'alembic' | 'abc' | 'c3d' | 'trc' | 'asf' | 'amc' | 'json' | 'internal'.

Each format has a corresponding file-structure interface for typed I/O. The per-format structures are:

  • BVHBVHFile (hierarchy: BVHHierarchy, motion: BVHMotion), BVHJoint, BVHChannel (XpositionZrotation), with BVHImportOptions / BVHExportOptions and DEFAULT_BVH_*_OPTIONS.
  • FBXFBXNode, FBXProperty, FBXTransform, FBXAnimationStack, FBXAnimationLayer, FBXAnimationCurve, FBXSkeleton; FBXVersion ('7100' … '7700'); FBXNodeType; import/export options + defaults.
  • glTF/GLBGLTFFile and the full glTF 2.0 object graph (GLTFNode, GLTFSkin, GLTFAnimation, GLTFAccessor, GLTFBufferView, …); GLB chunk magic constants GLB_CHUNK_JSON / GLB_CHUNK_BIN; import/export options.
  • USDUSDFile, USDPrim, USDAttribute, USDSkeleton, USDSkelAnimation, USDBlendShape; USDSchemaType; USDExportOptions.format is 'usda' | 'usdc' | 'usdz'; import/export options.
  • AlembicAlembicFile, AlembicObject, AlembicProperty, AlembicXformSample, AlembicPolyMeshSample, AlembicSkeletonData; AlembicExportOptions.backend is 'Ogawa' | 'HDF5'.
  • C3DC3DFile, C3DMarker, C3DAnalogChannel, C3DForcePlate, C3DEvent, C3DHeader; DEFAULT_C3D_IMPORT_OPTIONS (scaleFactor: 0.001, mm→m).
  • TRCTRCFile, TRCHeader, TRCMarker; DEFAULT_TRC_IMPORT_OPTIONS.
  • ASF/AMCASFFile, ASFBone, ASFHierarchy, AMCFile, AMCFrame; DEFAULT_ASF_AMC_IMPORT_OPTIONS.

The library's public entry point (index.ts) exports the functions importAnimation, exportAnimation, convertAnimation, and detectFormat(filenameOrContent) (returns MotionFormat | null). Three type maps associate each format with its structures and options: FormatFileTypeMap, FormatExportOptionsMap, and FormatImportOptionsMap. Note that FormatExportOptionsMap defines export options only for bvh, fbx, gltf/glb, usd*, and alembic/abc — C3D, TRC, and ASF/AMC have import options but no export options entry.

2.6 Motion Clip Database#

To make motion clips discoverable and reusable, the library provides a typed database record and a rich query model.

MotionClipEntry — a database record for a motion clip: id, name, optional description, category: MotionCategory, tags, duration, frameRate, frameCount, skeletonType: StandardSkeletonType, jointCount, sourceFormat: MotionFormat, optional filePath/fileSize, quality: QualityMetrics, metadata: AnimationMetadata, createdAt, updatedAt, version, optional parentId, variants.

MotionClipQuery supports filtering by name, category, tags (tagsMatchAll), duration range, skeletonType, minQuality, performer, action, date range, with limit/offset and sortBy ('name' | 'duration' | 'quality' | 'createdAt' | 'updatedAt') / sortOrder. MotionClipSearchResult returns entries, total, limit, offset. MotionClipCollection groups clip IDs.


3. Skeleton Models (@aja/skeleton-mapping)#

@aja/skeleton-mapping extends the core skeleton model with richer typing for retargeting operations. It re-exports the math and skeleton primitives from @aja/motion-formats and adds its own broader skeleton modeling needed for automatic bone matching, partial skeletons, and the normalized primal skeleton. Source: libs/aja/skeleton-mapping/src/types.ts.

3.1 Skeleton Templates#

StandardSkeletonType in this library is a wider union than in @aja/motion-formats, covering more conventions: animation-software standards (mixamo, unity-humanoid, unreal-mannequin, maya-hik, motionbuilder, blender-rigify), mocap formats (cmu, h36m, coco, openpose-body25, openpose-body18, mediapipe-pose, mediapipe-holistic), body models (smpl, smplx, star), fitness/sports skeletons (fitness-standard, yoga-enhanced, dance-extended), and custom.

SkeletonTemplate is the full definition used for matching and retargeting: id, name, type, description, joints: JointDefinition[], bones: BoneConnection[], rootJointId, defaultProportions: SkeletonProportions, regions: BodyRegion[], hasFingers, hasFacial, referenceHeight (meters), optional metadata.

JointDefinition provides the per-joint detail needed for automatic matching: id, name, aliases: string[], parentId (string | null), defaultPosition, defaultRotation (quaternion), region: BodyRegion, jointType: JointType, optional rotationLimits: RotationLimits, isEndEffector, index.

BoneConnection describes the relationship between two joints: parentJointId, childJointId, lengthRatio (relative to skeleton height), optional name.

BodyRegion classifies which part of the body a joint belongs to: 'root' | 'spine' | 'head' | 'left_arm' | 'right_arm' | 'left_hand' | 'right_hand' | 'left_leg' | 'right_leg' | 'left_foot' | 'right_foot' | 'face'.

JointType is a 50+-value union covering the full human anatomy. The values span core joints (root, pelvis, spine, chest, upper_chest, neck, head), arm joints, the complete set of finger joints (thumb_base/mid/tip, index_*, middle_*, ring_*, pinky_*), leg/foot joints, face joints (jaw, eye, eyebrow, nose, mouth, ear), and generic types (twist, auxiliary, unknown).

RotationLimits specifies anatomical range-of-motion constraints per axis: per-axis { min, max } for x, y, z (degrees).

SkeletonProportions captures 14 named body ratio fields — headRatio, neckRatio, torsoRatio, armRatio, forearmRatio, upperArmRatio, legRatio, thighRatio, shinRatio, shoulderWidthRatio, hipWidthRatio, armSpanRatio, handRatio, footRatio — used for proportional adaptation during retargeting. DEFAULT_PROPORTIONS provides average-adult values.

3.2 Skeleton Mapping#

A SkeletonMapping records how joints from one skeleton correspond to joints of another, with enough detail for the retargeter to handle merged joints, split joints, and procedurally synthesized joints.

SkeletonMapping fields: id, sourceTemplateId, targetTemplateId, sourceSkeletonType, targetSkeletonType, jointMappings: JointMapping[], confidence, scaleFactor, createdBy ('manual' | 'automatic' | 'hybrid'), createdAt, optional metadata.

JointMapping describes one source-to-target joint connection: sourceJointId, targetJointId, confidence (0–1), mappingType: JointMappingType, optional weight, rotationOffset, positionOffset, localScale, additionalSources/additionalTargets, blendWeights.

JointMappingType specifies the relationship kind: 'direct' | 'merged' | 'split' | 'interpolated' | 'procedural' | 'ignored' | 'synthesized'.

3.3 Automatic Matching#

The automatic matcher combines four complementary strategies to handle skeleton variations it has never seen before. AutoMatchConfig toggles each strategy independently (useNameMatching, useHierarchyMatching, usePositionalMatching, useSemanticMatching) and sets minConfidence, plus fine-grained options for each strategy:

  • NameMatchOptions: fuzzy matching, threshold, name normalization, alias use, custom mappings.
  • HierarchyMatchOptions: match by depth/sibling/child count, depthTolerance.
  • PositionalMatchOptions: position normalization, distance threshold, proportional matching.

DEFAULT_AUTO_MATCH_CONFIG enables all four strategies with minConfidence: 0.5 and fuzzyThreshold: 0.8.

AutoMatchResult carries the full outcome of a matching run: mapping, statistics: MatchStatistics, matchDetails: JointMatchDetail[], unmatchedSourceJoints, unmatchedTargetJoints, warnings. MatchStatistics records how many joints were matched by each of the four strategies. JointMatchDetail records per-joint matchMethod ('name' | 'hierarchy' | 'position' | 'semantic' | 'manual' | 'none') and the four strategy sub-scores.

3.4 Partial and Primal Skeletons#

Partial skeletons allow retargeting only a subset of the body — for example, applying captured upper-body motion to a procedurally animated lower body.

PartialSkeletonConfig specifies the subset: includedRegions: BodyRegion[], includeIKTargets, boundaryHandling ('include' | 'exclude' | 'blend'), preserveHierarchy, optional customRootJoint. PartialSkeleton carries the extracted joints, bones, root, and boundaryJoints: BoundaryJoint[].

PartialSkeletonPreset names the common presets: 'upper_body' | 'lower_body' | 'torso_only' | 'arms_only' | 'left_arm_only' | 'right_arm_only' | 'legs_only' | 'left_leg_only' | 'right_leg_only' | 'hands_only' | 'face_only' | 'head_and_hands' | 'full_body'.

The primal skeleton is a topology-agnostic 18-joint humanoid skeleton used as a normalization intermediate. PrimalSkeleton represents it; PrimalJointId enumerates its 18 joints (root, pelvis, spine, chest, neck, head, and left/right shoulder/elbow/wrist and hip/knee/ankle). PrimalMapping maps any template to and from this primal form. Supporting constants: PRIMAL_BONE_LENGTHS (primal bone length ratios) and REGION_JOINTS (maps each BodyRegion to its constituent JointTypes).


4. Motion Quality Models (@aja/motion-quality)#

Source: libs/aja/motion-quality/src/types.ts.

Quality assessment in Aja is multi-dimensional: technical metrics (noise, foot sliding, bone length variance), perceptual metrics (naturalness, style consistency), and ground-truth benchmark metrics (MPJPE) are all first-class types. The library is organized into four source files corresponding to these dimensions.

QualityRating is the human-readable quality category: 'excellent' | 'good' | 'acceptable' | 'poor' | 'unusable'.

QualityThresholds defines the score cutoffs: excellent (default 0.90), good (0.75), acceptable (0.60), poor (0.40); anything below poor is unusable.

MotionCategory (this library's variant) covers body-part subsets in addition to activity categories: 'locomotion' | 'dance' | 'combat' | 'sports' | 'gesture' | 'facial' | 'full_body' | 'upper_body' | 'lower_body' | 'general'.

JitterAnalysis is the detailed output of jitter measurement: score (0–1, lower better), averageAcceleration, peakAcceleration, perJointScores: Map<string, number>, problematicFrames: number[], optional dominantFrequency (Hz).

The four source files are: quality-metrics.ts (technical quality metrics), perceptual-metrics.ts (naturalness, style consistency), ground-truth- comparison.ts (MPJPE, PA-MPJPE, PCK, AUC), and qa-pipeline.ts (automated QA pipeline configuration and reporting).


5. Motion Pipeline Service — Domain Objects#

Source: apps/aja/svc-motion-pipeline/src/types.ts (and config/, jobs/, delivery/). The service file headers label themselves @lilith/svc-motion-pipeline.

The pipeline service is the job-management heart of Aja. Its domain objects represent the full lifecycle of a motion processing job: configuration, job submission, stage-by-stage execution, and result delivery.

5.1 Pipeline Domain and Stage Enums#

These core enums define what kind of work the pipeline does and what states it can be in. Every job submission references a PipelineDomain and QualityPreset; every running job tracks a JobStatus and per-stage StageStatus.

PipelineDomain'yoga' | 'fitness' | 'dance' | 'martial-arts' | 'general'.

QualityPreset'draft' | 'standard' | 'high' | 'ultra'.

PipelineStage (11 stages) — 'ingestion' | 'validation' | 'preprocessing' | 'pose-estimation' | 'skeleton-fitting' | 'domain-analysis' | 'quality-assessment' | 'retargeting' | 'format-conversion' | 'postprocessing' | 'delivery'.

StageStatus'pending' | 'running' | 'completed' | 'failed' | 'skipped'.

JobPriority'critical' | 'high' | 'normal' | 'low' | 'background'.

JobStatus'pending' | 'queued' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled' | 'timeout'.

5.2 Pipeline Configuration#

A PipelineConfiguration is the complete description of how the pipeline should process a job — which stages are enabled, what quality preset applies, input constraints, and output format specification.

PipelineConfiguration fields: id, name, description, version, domain: PipelineDomain, qualityPreset: QualityPreset, stages: StageConfig[], inputConstraints: InputConstraints, outputSpec: OutputSpec, globalTimeoutMs, failFast, metadata, createdAt, updatedAt.

StageConfig controls one stage: name: PipelineStage, enabled, params, timeoutMs, retry: StageRetryConfig, dependencies: PipelineStage[], parallel.

StageRetryConfig: maxAttempts, initialDelayMs, maxDelayMs, backoffMultiplier.

InputConstraints: acceptedFormats, minFrameRate, maxFrameRate, minDurationSec, maxDurationSec, maxFileSizeBytes, optional requiredSkeletonType.

OutputSpec: primaryFormat, secondaryFormats, includeIntermediates, compression: CompressionSettings, includeAnalytics, includeQualityMetrics. CompressionSettings.algorithm is 'none' | 'gzip' | 'lz4' | 'zstd', level 1–9.

QualityPresetConfig captures the parameters for one preset: name, description, processingTimeMultiplier, targetQuality (0–1), resolutionScale, targetFrameRate, features: QualityFeatures. QualityFeatures has eight boolean toggles: noiseReduction, motionSmoothing, jitterCorrection, footSlidingCorrection, contactDetection, physicsRefinement, ikCleanup, multiViewFusion.

The four built-in presets (from config/pipeline-config.ts), seeded into the service on startup, cover the full quality/speed range:

Preset processingTimeMultiplier targetQuality resolutionScale targetFrameRate
draft 0.3 0.6 0.5 15
standard 1.0 0.8 1.0 30
high 2.0 0.92 1.0 60
ultra 4.0 0.98 1.0 (60+)

draft enables no quality features. standard enables noise reduction, motion smoothing, jitter correction, and IK cleanup. high adds foot-sliding correction and contact detection. ultra is the maximum-quality preset.

On startup, the service also seeds 20 default configurations — one for every PipelineDomain × QualityPreset combination (5 domains × 4 presets), named {domain}-{preset}.

5.3 YAML Pipeline Definitions#

Pipeline configurations can be written as YAML files and imported or exported via the API. The YAML schema is typed as PipelineDefinition: apiVersion, kind: 'PipelineDefinition', metadata: PipelineDefinitionMetadata, spec: PipelineSpec.

PipelineSpec holds domain, qualityPreset, stages: StageSpec[], input: InputSpec, output: OutputSpecification, settings: GlobalSettings. Supporting types: StageSpec, RetrySpec (backoff: 'exponential' | 'linear' | 'fixed'), InputSpec, FrameRateSpec, DurationSpec, and OutputSpecification.

The conversion functions parsePipelineDefinition and serializePipelineDefinition convert between PipelineConfiguration and YAML.

5.4 Job Objects#

These types cover the complete lifecycle of a submitted job, from initial request through execution state to final results.

JobSubmissionRequest: optional jobId, pipelineConfig (string | PipelineConfiguration), input: JobInput, options: JobOptions, optional webhook: WebhookConfig, optional metadata.

JobInput: sourceType ('url' | 'upload' | 's3' | 'reference'), source, optional formatHint, params.

JobOptions: priority: JobPriority, optional timeoutMs, tags, scheduledAt, idempotencyKey, streamProgress, parentJobId, notifyOnComplete.

JobEnvelope is the full job state object returned by the API: id, tenantId, userId, status: JobStatus, pipelineConfig: PipelineConfiguration, input, options, progress: JobProgress, stages: StageExecution[], optional result: JobResult, optional error: JobError, optional webhook, metadata, stats: JobStats, createdAt, optional startedAt/completedAt, updatedAt.

JobProgress: percentage (0–100), optional currentStage, stagePercentage, message, optional estimatedRemainingMs, framesProcessed, totalFrames.

StageExecution tracks one stage's run: stage: PipelineStage, status: StageStatus, progress, attempt, optional startedAt/completedAt/durationMs/outputRef, optional error: StageError, optional metrics.

StageError: code, message, optional stack, retryable: boolean.

JobStats tracks resource usage: totalDurationMs, queueWaitMs, retryCount, peakMemoryBytes, cpuTimeMs, gpuTimeMs, inputSizeBytes, outputSizeBytes.

JobError: code, message, optional stage, stack, details, retryable, timestamp.

JobResult holds the deliverables: primaryOutput: OutputReference, secondaryOutputs: OutputReference[], optional analytics: AnalyticsResult, qualityMetrics: QualityMetrics, intermediates: IntermediateResult[].

OutputReference: format, storageType ('local' | 's3' | 'gcs' | 'azure'), path, sizeBytes, checksum, optional downloadUrl, urlExpiresAt.

AnalyticsResult: domainAnalysis, motionStats: MotionStatistics, qualityAssessment: QualityAssessment. MotionStatistics carries duration, frame count, frame rate, joint count, rootVelocity: VelocityStats (mean/max/min/std), and rangeOfMotion.

QualityAssessment: overallScore (0–100), smoothness, naturalness, jitterAmount, footSliding, penetrations, issues: QualityIssue[]. QualityIssue: type, severity ('low' | 'medium' | 'high'), frameStart/frameEnd, description.

QualityMetrics (service variant): optional mpjpe, paMpjpe, pck, custom.

For list views, JobSummary is the lightweight projection: id, status, domain, progress, priority, tags, createdAt, updatedAt.

JobListQuery supports filtering by status[], domain[], tags[], userId, parentJobId, created-after/before, sortBy ('createdAt' | 'updatedAt' | 'priority' | 'status'), sortOrder, page, pageSize. JobListResponse returns jobs: JobSummary[], total, page, pageSize, totalPages.

BatchOperationRequest: jobIds, operation ('cancel' | 'retry' | 'prioritize'), optional params. BatchOperationResponse returns succeeded and failed: BatchOperationFailure[].

5.5 Result Delivery Objects#

These types handle the outbound side: notifying callers via webhooks and providing signed URLs for downloading results.

WebhookConfig: url, method ('POST' | 'PUT'), optional headers, secret, events: WebhookEvent[], retry: WebhookRetryConfig.

WebhookEvent is the union of all events that can be delivered to a registered webhook endpoint — 9 values: 'job.queued' | 'job.started' | 'job.progress' | 'job.completed' | 'job.failed' | 'job.cancelled' | 'stage.started' | 'stage.completed' | 'stage.failed'. See §11 for per-event data shapes.

WebhookPayload: event, eventId, jobId, timestamp, data: WebhookEventData, delivery: WebhookDeliveryMeta. WebhookEventData is a discriminated union over per-event shapes (JobQueuedEventData, JobStartedEventData, JobProgressEventData, JobCompletedEventData, JobFailedEventData, JobCancelledEventData, StageEventData).

WebhookDeliveryStatus tracks delivery state: deliveryId, eventId, jobId, event, status ('pending' | 'in_progress' | 'delivered' | 'failed' | 'retrying'), attempts, optional lastAttemptAt/nextRetryAt/ responseStatus/errorMessage, createdAt.

For polled job status, PollResponse is { job: JobEnvelope, polling: PollingHints }. PollingHints carries recommendedIntervalMs, isActive, optional estimatedCompletionTime, serverTime.

For streaming, ProgressStreamMessage (type: 'progress' | 'stage' | 'complete' | 'error' | 'keepalive') carries SSE payloads.

For downloads, DownloadRequest / DownloadResponse / DownloadMetadata cover signed-URL generation. DownloadMetadata.checksumAlgorithm is 'sha256' | 'md5'.

5.6 Service Configuration Objects#

These objects are passed to the service at startup to configure its runtime dependencies.

ServiceConfig is the top-level service configuration: serviceName, port, redis: RedisConfig, storage: StorageConfig, queue: QueueConfig, webhook: WebhookServiceConfig, rateLimit: RateLimitConfig, metrics: MetricsConfig.

The nested configs are:

  • RedisConfig: host, port, optional password, db, keyPrefix.
  • StorageConfig: type ('local' | 's3' | 'gcs' | 'azure'), optional endpoint, bucket, optional accessKey/secretKey/region, pathPrefix, signedUrlExpiration.
  • QueueConfig: namePrefix, concurrency, defaultTimeoutMs, maxRetries, cleanupAfterMs.
  • WebhookServiceConfig: maxConcurrent, deliveryTimeoutMs, maxPayloadSize, signatureAlgorithm ('sha256' | 'sha512').
  • RateLimitConfig: maxRequests, windowMs, perTenant.
  • MetricsConfig: enabled, endpoint, latencyBuckets.

QueueStatistics, ServiceHealth (status: 'healthy' | 'degraded' | 'unhealthy'), and ComponentHealth cover runtime observability.

5.7 Pipeline Processing State and V2 Deferral#

The job processor is createPipelineProcessor() in apps/aja/svc-motion-pipeline/src/jobs/pipeline-processor.ts (authored under task V1-P2-1775). It drives each enabled stage and, for V1, executes ingestion, validation, preprocessing, quality-assessment, format-conversion, postprocessing, and delivery using @aja/motion-formats for ingest/export.

Four stages are V2-deferred per descope decision V1-P2-0331. The constant V2_DEFERRED_STAGES is the set { 'pose-estimation', 'skeleton-fitting', 'domain-analysis', 'retargeting' }. When a pipeline configuration enables one of these stages, the processor marks it 'skipped' and emits a structured log line citing V1-P2-0331 — it never silently fails. The naturalness field of QualityAssessment is pinned to the smoothness score as a physical proxy because a real naturalness score requires a V2 ML model.


6. Motion Pipeline Service — HTTP API#

The motion pipeline service is a Fastify server built via @lilith/fastify-core (apps/aja/svc-motion-pipeline/src/app.ts). All routes are under the /v1/pipeline/... prefix except /health. Tenant and user identity are read from the x-tenant-id and x-user-id request headers, defaulting to default / anonymous. There is no dedicated auth middleware in code; authorization is header-driven.

6.1 Health and Statistics#

Method Path Purpose
GET /health ServiceHealth — component health + uptime
GET /v1/pipeline/stats QueueStatistics — waiting/active/completed/failed counts

6.2 Configuration Routes#

Pipeline configurations can be created from presets, from a JSON body, or from a YAML definition, and can be exported back to YAML.

Method Path Purpose
GET /v1/pipeline/configs List configs (filter domain, qualityPreset)
GET /v1/pipeline/configs/:configId Get config by ID
GET /v1/pipeline/configs/by-name/:name Get config by name
POST /v1/pipeline/configs Create config from domain/qualityPreset
POST /v1/pipeline/configs/yaml Create config from a YAML body
GET /v1/pipeline/configs/:configId/yaml Export a config as YAML (text/yaml)
DELETE /v1/pipeline/configs/:configId Delete a config (204 on success)
GET /v1/pipeline/presets List the four QUALITY_PRESETS

6.3 Job Routes#

Method Path Purpose
POST /v1/pipeline/jobs Submit a job; returns 202 with jobId, pollUrl, streamUrl
GET /v1/pipeline/jobs/:jobId Poll job — returns PollResponse
GET /v1/pipeline/jobs List jobs for the tenant (JobListQuery)
POST /v1/pipeline/jobs/:jobId/cancel Cancel a job
POST /v1/pipeline/jobs/:jobId/retry Retry a failed job
PATCH /v1/pipeline/jobs/:jobId/priority Update job priority
POST /v1/pipeline/jobs/batch Batch cancel / retry / prioritize

6.4 Download Routes#

Outputs are only accessible after a job reaches completed status. Download URLs are time-limited signed URLs; callers should not store them permanently.

Method Path Purpose
GET /v1/pipeline/jobs/:jobId/outputs List available outputs (job must be completed)
POST /v1/pipeline/jobs/:jobId/download Generate a signed download URL

6.5 Webhook Routes#

Method Path Purpose
GET /v1/pipeline/jobs/:jobId/webhooks List webhook deliveries for a job
GET /v1/pipeline/webhooks/:deliveryId Get a webhook delivery status
POST /v1/pipeline/webhooks/:deliveryId/retry Retry a webhook delivery
GET /v1/pipeline/webhooks/stats Webhook delivery statistics

6.6 Streaming Routes (Server-Sent Events)#

Job progress is delivered over Server-Sent Events (text/event-stream) — not WebSocket. The stream emits an initial connected message, then progress, stage, complete, and error messages as the job runs. The stream endpoint requires the job to be in an active state (pending, queued, or running).

Method Path Purpose
GET /v1/pipeline/jobs/:jobId/stream SSE stream of job progress (job must be active)
GET /v1/pipeline/streams/stats Active SSE connection count

Error responses across the API use a JSON body { code, message } (with optional errors for validation). Defined error codes include: CONFIG_NOT_FOUND, JOB_NOT_FOUND, JOB_NOT_COMPLETE, JOB_NOT_ACTIVE, INVALID_CONFIG, YAML_PARSE_ERROR, JOB_SUBMISSION_FAILED, CANCELLATION_FAILED, RETRY_FAILED, UPDATE_FAILED, DOWNLOAD_FAILED, DELIVERY_NOT_FOUND.


7. Motion AI Service — HTTP API#

The Motion AI service is a Hono application (apps/aja/svc-motion-ai/src/app.ts) that listens on PORT (default 3040). It applies cors() and logger() middleware globally. There is no auth middleware in code.

The service composes five internal services: VideoToMotionService, MotionRetargetingService, SkeletalAnimationMotionService, VideoAnalysisService, and EmbodiedInstructionService (the embodied service depends on the skeletal-animation and video-analysis services).

Method Path Purpose
GET /health { status: 'healthy', service: 'aja-motion-ai' }
GET /api/v1/embodied-instruction/capabilities EmbodiedInstructionCapabilities — version, domains, endpoints
POST /api/v1/video-to-motion/process Process a video into motion (videoSource, sourceType, options)
POST /api/v1/retargeting/retarget Retarget a motion clip (motionId, targetSkeletonId, config)
POST /api/v1/skeletal/animate Generate a procedural idle frame (config, deltaTime)
POST /api/v1/analysis/analyze Run video analysis
POST /api/v1/embodied-instruction/demonstration-plan Build structured study moments from a coached demonstration
POST /api/v1/embodied-instruction/coaching-overlay Build a real-time coaching overlay
POST /api/v1/embodied-instruction/session-handoff Build a session handoff to a downstream destination

7.1 Embodied-Instruction Request Validation (Zod)#

Three of the embodied-instruction endpoints validate the request body with Zod schemas (demonstrationRequestSchema, coachingOverlaySchema, sessionHandoffSchema). On failure they return HTTP 400 with { error, issues } where error is one of INVALID_DEMONSTRATION_REQUEST, INVALID_COACHING_OVERLAY_REQUEST, or INVALID_SESSION_HANDOFF_REQUEST.

The validated enum values shared across these schemas are:

  • domain'fitness' | 'yoga' | 'dance' | 'martial-arts' | 'sports' | 'rehabilitation'.
  • demonstration modality'live' | 'recorded' | 'annotated'.
  • coaching learnerStage'novice' | 'developing' | 'advanced' | 'expert'.
  • session-handoff destination'lesson_path' | 'study_pack' | 'tutoring'.
  • capture format'mp4' | 'webm' | 'mov'.

getCapabilities() reports apiVersion, stability: 'stable', service: 'aja-motion-ai', the supported domains, and the embodied-instruction endpoint list with method, path, and purpose.

7.2 Service-Internal AI Modules#

Beyond the exposed routes, svc-motion-ai/src/ contains internal module trees wired by the five services. These modules are not all directly exposed as routes:

  • video-to-motion/ — video preprocessing, multi-model pose estimation (PoseEstimationModel, MediaPipePoseLandmark, MoveNetKeypoint, OpenPoseBody25Keypoint, UnifiedKeypointName, HandKeypoint), temporal processing, keypoint normalization.
  • motion-retargeting/, skeletal-animation-motion/, video-analysis/, embodied-instruction/.
  • live-motion-capture/ — webcam capture, real-time pose estimation, streaming pose lift, real-time retargeting, live avatar driving.
  • mobile-live-capture/ — iOS and Android capture, on-device processing, mobile streaming.
  • motion-enhancement/ — motion synthesis, motion style transfer, motion super-resolution, physics refinement. Key types in this module: DiffusionModelType, RotationRepresentation, MotionSequence, MotionTensor.
  • providers/video-router.ts — routes video input to the appropriate processing path.

8. Reference Video Service — Domain Objects#

Source: apps/aja/svc-reference-video/src/types.ts. Package @lilith/svc-reference-video. This module exports the reference-video subsystem: video ingestion, scene detection, metadata extraction, categorization, search, annotation, and collaboration. Being a library rather than a server, it is imported by other Aja code rather than called over HTTP.

8.1 Branded Identifiers#

The reference video subsystem uses branded string IDs throughout for type safety. There are 12 ID types, each with a matching generate*Id factory: VideoId, SegmentId, AnnotationId, ClipId, CategoryId, TagId, WorkspaceId, UserId, CommentId, ReviewId, AssignmentId, VersionId, EmbeddingId.

8.2 Video Metadata#

The metadata model covers full ffprobe-style stream descriptions for any ingested video.

VideoCodec'h264' | 'h265' | 'vp8' | 'vp9' | 'av1' | 'prores' | 'dnxhd' | 'unknown'. AudioCodec'aac' | 'mp3' | 'opus' | 'vorbis' | 'pcm' | 'ac3' | 'unknown'. ContainerFormat'mp4' | 'mov' | 'mkv' | 'webm' | 'avi' | 'mxf' | 'ts' | 'unknown'. ColorSpace'bt709' | 'bt2020' | 'srgb' | 'p3' | 'rec601' | 'unknown'. PixelFormat'yuv420p' | 'yuv422p' | 'yuv444p' | 'rgb24' | 'rgba' | 'unknown'.

VideoStreamMetadata, AudioStreamMetadata, and VideoMetadata capture the full per-stream and file-level metadata.

8.3 Video Entity#

VideoSourceType describes where a video came from: 'upload' | 'url_import' | 'youtube' | 'vimeo' | 'clip_extraction' | 'processed'.

VideoStatus tracks the processing lifecycle: 'uploading' | 'processing' | 'transcoding' | 'analyzing' | 'ready' | 'error' | 'archived'.

Video is the top-level entity: id, name, optional description, originalFilename, sourceType, optional sourceUrl, status, optional errorMessage, workspaceId, createdBy, timestamps, optional metadata, originalPath, optional hlsManifestUrl/dashManifestUrl, renditions: VideoRendition[], optional posterUrl, optional thumbnailSprite: ThumbnailSprite, categoryIds, tagIds, currentVersionId, optional parentVideoId, isArchived, customMetadata.

8.4 Scene, Person, and Activity Detection#

Aja automatically analyzes ingested videos to identify scene cuts, detect people, and classify activities — making the library searchable without manual annotation.

SceneDetectionAlgorithm'content_aware' | 'adaptive_content' | 'threshold' | 'histogram' | 'deep_learning'. SceneTransitionType'cut' | 'fade_in' | 'fade_out' | 'dissolve' | 'wipe' | 'unknown'. DetectedScene, SceneDetectionConfig, and SceneDetectionResult model scene cuts.

DetectedPerson, ActivityClassification, PersonDetectionResult, and ActivityDetectionResult model person tracking and activity classification.

8.5 Segments, Annotations, and Clips#

SegmentType classifies regions within a video: 'scene' | 'shot' | 'action' | 'movement' | 'rest' | 'transition' | 'repetition' | 'custom'. Segment is a named temporal region with frame/time bounds and labels.

AnnotationType enumerates the spatial forms an annotation can take: 'point' | 'bounding_box' | 'polygon' | 'polyline' | 'skeleton' | 'label' | 'text' | 'timestamp' | 'correction'. An Annotation carries the type-specific payload as boundingBox: BoundingBox, points: PolygonPoint[], skeleton: SkeletonPose, or text. SkeletonPose holds format, joints: SkeletonJoint[], confidence, is3D.

Clip is an extracted region of a source video with extraction status ('pending' | 'processing' | 'ready' | 'error').

8.6 Categories, Tags, Search, and Collaboration#

Category is hierarchical: each category has a parentId, path, and depth. Tag carries a normalized name and usage count. AutoTaggingConfig governs the automatic tagging pipeline.

The search system uses vector embeddings for semantic retrieval. EmbeddingType identifies the model used: 'clip_visual' | 'clip_text' | 'videomae' | 'optical_flow' | 'motion_signature' | 'audio'. VideoEmbedding stores a Float32Array vector with dimension, modelId, modelVersion.

SearchQuery supports four search modes — text, visual, motion, and hybrid — with filters, pagination, and sort. SearchResult/SearchResultItem carry scored results. Three backends are present in tests: video-search-clip.test.ts (CLIP embeddings), video-search-meilisearch.test.ts (text search via Meilisearch), and video-search-qdrant.test.ts (vector search via Qdrant).

The collaboration model covers workspaces, review workflows, and version history:

WorkspaceRole'owner' | 'admin' | 'editor' | 'reviewer' | 'viewer'. Workspace, WorkspaceSettings, and WorkspaceMember model collaboration spaces.

Comment, Review (ReviewStatus: 'pending' | 'in_progress' | 'needs_revision' | 'approved' | 'rejected'), and Assignment (AssignmentType, AssignmentStatus, AssignmentPriority) model review workflows.

Version (VersionType: 'video' | 'annotation' | 'segment' | 'metadata') models the version history of any of these entities.

8.7 Errors#

ReferenceVideoErrorCode is a TypeScript enum with values organized by category. General codes include UPLOAD_FAILED, UNSUPPORTED_FORMAT, and FILE_TOO_LARGE. Processing codes include PROCESSING_FAILED and TRANSCODING_FAILED. Analysis codes include SCENE_DETECTION_FAILED. Search codes include SEARCH_FAILED. Collaboration codes include WORKSPACE_NOT_FOUND. Version codes include VERSION_NOT_FOUND and ROLLBACK_FAILED. ReferenceVideoError is the corresponding Error subclass with code and details.


9. Cross-Domain Integration (@aja/motion-integration)#

@aja/motion-integration provides the typed contracts and adapter implementations for moving Aja motion data into other Oshun domains. Rather than allowing downstream domains to import Aja's internal types directly, all data crosses the domain boundary through these adapters. Source: libs/aja/motion-integration/src/. The library file header labels itself "cross-domain integration adapters for Lilith motion pipeline."

IntegrationDomain'yemaya' | 'isis' | 'bellona' | 'sophia'.

AdapterStatus'disconnected' | 'connecting' | 'connected' | 'error'.

Branded IDs used across all adapters: MotionAssetId, MotionProjectId, MotionSessionId, MotionModelId, MotionJobId, MotionKnowledgeId, MotionCitationId.

BaseAdapterConfig is common adapter configuration: domain, endpoint, optional auth: AuthConfig, timeoutMs, retry: RetryConfig, debug. AuthConfig.type is 'api-key' | 'bearer' | 'oauth2' | 'none'.

Shared exported types include MotionDataFormat, MotionDomainType, MotionQualityLevel, MotionAssetMetadata, MotionSessionMetadata, SubjectInfo, CaptureEnvironment, SessionQualityMetrics, IntegrationEventType, IntegrationEvent, IntegrationEventHandler, IntegrationResult, IntegrationError, PaginationOptions, PaginatedResponse, SortOptions, DateRangeFilter, CrossDomainReference, LinkedAsset, and the IntegrationAdapter interface.

The four adapter modules are each implemented as a separate directory with adapter.ts, index.ts, and types.ts: bellona/, isis/, sophia/, yemaya/. They are importable from subpaths (e.g., @aja/motion-integration/yemaya).


10. Motion Pipeline SDK (@aja/motion-pipeline-sdk)#

The TypeScript SDK is the recommended way for Node.js applications to interact with the motion pipeline service. It provides a fully typed client, builder patterns for configuration and job submission, and SSE streaming for progress monitoring. Source: libs/aja/motion-pipeline-sdk/src/.

10.1 Client#

The exported client class is MotionPipelineClient (not AjaClient). The SDK also exports ConfigBuilder, JobBuilder, and a createClient(config) convenience function. MotionPipelineClient is constructed with an SDKConfig.

SDKConfig fields: baseUrl (required), optional auth: AuthConfig, tenantId, userId, timeoutMs, retry: RetryConfig, debug, custom fetch, custom headers. DEFAULT_SDK_CONFIG sets timeoutMs: 30000 and a retry policy of 3 attempts, exponential backoff (multiplier: 2, 1s initial, 30s max), retrying on status codes 408, 429, 500, 502, 503, 504.

AuthType'api-key' | 'bearer' | 'oauth2' | 'none'. For api-key the client sends Authorization: ApiKey <key>; for bearer/oauth2 it sends Authorization: Bearer <token>. OAuth2 uses the client-credentials grant and caches the token with a 30-second expiry buffer.

10.2 Client Operations#

All MotionPipelineClient methods return SDKResult<T> — a discriminated union SuccessResult<T> | ErrorResult. The client covers every route exposed by the pipeline service (§6):

  • Health/stats: health(), getStats().
  • Config: listConfigs(), getConfig(), getConfigByName(), createConfig(), createConfigFromYaml(), exportConfigAsYaml(), deleteConfig(), getQualityPresets().
  • Jobs: submitJob(), getJob(), listJobs(), cancelJob(), retryJob(), updateJobPriority(), batchOperation().
  • Downloads: listOutputs(), generateDownloadUrl().
  • Webhooks: getWebhookDeliveries(), getWebhookDeliveryStatus(), retryWebhookDelivery(), getWebhookStats().
  • Streaming: streamJobProgress() (Server-Sent Events via EventSource), closeAllStreams(), activeStreamCount.
  • Convenience: submitAndWait() (polls until terminal state), configBuilder(), jobBuilder().

10.3 SDK Domain Types#

The SDK mirrors the service's domain types with minor differences noted below.

PipelineDomain, QualityPreset, PipelineStage, and StageStatus mirror the service. The SDK's JobStatus is the narrower set: 'pending' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' (no paused or timeout). JobPriority matches the service.

Branded IDs: JobId, ConfigId, DeliveryId, TenantId, UserId, with constructors jobId(), configId(), deliveryId().

Job-side types: VideoInputSource (InputSourceType: 'url' | 'upload' | 'storage' | 'stream'), WebhookConfig (SDK events: 'queued' | 'started' | 'progress' | 'completed' | 'failed' | 'cancelled'), JobOptions, JobSubmitRequest, Job, JobResult, StageResult, JobListQuery, JobSubmitResponse, JobListResponse, JobCancelRequest, BatchOperationRequest (BatchOperationType: 'cancel' | 'retry' | 'update-priority'), BatchOperationResult.

StreamEventType enumerates the 12 SSE event types observable on the /v1/pipeline/jobs/:jobId/stream channel: 'connected' | 'job:queued' | 'job:started' | 'job:progress' | 'job:completed' | 'job:failed' | 'job:cancelled' | 'stage:started' | 'stage:progress' | 'stage:completed' | 'stage:failed' | 'heartbeat' | 'error'. StreamEvent<T> carries a type-mapped data payload per event type.

SDKErrorCodes is a constant map of SDK error codes: SDK_INVALID_CONFIG, SDK_INVALID_REQUEST, SDK_TIMEOUT, SDK_STREAM_ERROR, SDK_AUTH_FAILED, SDK_AUTH_EXPIRED, SDK_PERMISSION_DENIED, SDK_NOT_FOUND, SDK_CONFLICT, SDK_RATE_LIMITED, SDK_SERVICE_UNAVAILABLE, SDK_INTERNAL_ERROR, SDK_NETWORK_ERROR, SDK_CONNECTION_FAILED. SDKResult carries isSuccess, isError, and unwrap helpers.

10.4 Python SDK (aja-motion-pipeline)#

libs/aja/motion-pipeline-sdk-python is a Python distribution (pyproject.toml name aja-motion-pipeline, import package aja_motion_pipeline). Its modules provide sync and async clients plus specialized helpers:

  • client.py / async_client.py — synchronous and asynchronous clients.
  • types.py / errors.py — typed request/response models and error classes.
  • batch.py — batch submission helpers.
  • data_science.py / jupyter.py — data science and Jupyter notebook integration.
  • training.py — ML training workflow helpers.

The package marks py.typed for type checker support.


11. Event Model#

Aja does not publish to a named domain event bus. There is no aja.* event topic. The domain's eventing is in-process and delivery-channel-specific, split across three channels: an internal EventEmitter for job lifecycle, outbound HTTP webhook deliveries to caller-specified endpoints, and Server-Sent Events streamed to the API caller.

11.1 Job-Manager Events (In-Process EventEmitter)#

apps/aja/svc-motion-pipeline/src/jobs/job-manager.ts defines a JobEvent union and an on(event, handler) subscription interface. The service's app.ts subscribes to these events to drive webhook deliveries and SSE broadcasts.

The 10 in-process events are: 'job:created', 'job:queued', 'job:started', 'job:progress', 'job:stage:started', 'job:stage:completed', 'job:stage:failed', 'job:completed', 'job:failed', 'job:cancelled'.

Each handler receives the JobEnvelope and optional data. Stage events carry { stage } (started/completed) or { stage, error } (failed).

11.2 Webhook Events (Outbound HTTP)#

WebhookEvent is the set of events deliverable to caller-registered webhook URLs — a subset of the in-process events, throttled and reformatted for HTTP delivery. There are 9 deliverable webhook events:

Event Data shape Notes
job.queued JobQueuedEventData queuePosition, optional estimatedStartTime
job.started JobStartedEventData startedAt, estimatedDurationMs
job.progress JobProgressEventData throttled — delivered only at 25 / 50 / 75 %
job.completed JobCompletedEventData result, stats
job.failed JobFailedEventData error, retryable
job.cancelled JobCancelledEventData optional reason, cancelledBy
stage.started StageEventData stage, execution
stage.completed StageEventData stage, execution
stage.failed StageEventData stage, execution

Webhook progress events are throttled to the 25 %, 50 %, and 75 % marks only — not on every progress update — to avoid flooding the receiving endpoint.

11.3 Stream Events (Server-Sent Events)#

The SSE stream (/v1/pipeline/jobs/:jobId/stream) delivers the SDK's StreamEventType values (§10.3) in real time as the job progresses. The stream includes connected and heartbeat events in addition to the job:* and stage:* lifecycle events.

11.4 Cross-Domain Integration Events#

@aja/motion-integration exports IntegrationEventType, IntegrationEvent, and IntegrationEventHandler for adapter-level event subscription within the integration layer. These are integration-adapter events scoped to the adapter lifecycle — not a shared domain event bus.


11A. V2 Cross-Domain Contracts (Aja → Bellona)#

The sister-monorepo V2 fighting-game project consumes Aja motion data through two reciprocal contract services that live under V2/services/. Both are thin contract layers that depend on real Aja libraries (@aja/consent-management) and Bellona libraries (@bellona/mocap, @bellona/unreal) rather than re-implementing them. They exist so that the Aja → Bellona → Unreal handoff is deterministic, auditable, and gated on performer rights before any capture data is processed.

@v2/aja-bellona-livelink-export (apps/v2/aja-bellona-livelink-export/src/livelink-export.ts) builds the export plan that carries an Aja-retargeted skeleton clip into Unreal as a frame-aligned animation asset. The contract route is @aja → @bellona/mocap → @bellona/unreal: Aja produces the markerless, neural-retargeted motion; @bellona/mocap resamples and frame-snaps it; and @bellona/unreal receives the editor-only Live Link handoff that imports and cooks the AnimSequence.

The pipeline is built around determinism. Markerless capture and neural retargeting produce continuous-time motion, but a rollback-netcode fighting game cannot tolerate sub-frame jitter, so @bellona/mocap (createBellonaFrameSnapLiveLinkExport) resamples every accepted clip to a fixed 60 Hz grid and assigns integer frame numbers. Notify segments — hit-active, armor, cancel-window, invulnerability, root-motion, and foot-contact windows — are normalized to integer 60 Hz frame boundaries (integer-60hz-frame-boundaries); any segment that resolves off a frame boundary beyond the 0.5 ms tolerance is rejected rather than silently rounded. The export plan therefore guarantees that two clients replaying the same input sequence land on identical frame numbers for every gameplay-relevant event.

The build plan (buildV2AjaBellonaLiveLinkExportPlan) drives four stages:

  1. aja_retargeted_motion_acceptance — accepts the Aja-retargeted motion (URI + sha256: content hash) for the target V2 skeleton.
  2. bellona_mocap_frame_snap_export — produces the BellonaFrameSnapLiveLinkManifest at targetFrameRate: 60.
  3. bellona_unreal_live_link_handoff — hands the subject off on the Bellona mocap Live Link port for editor-time import.
  4. ue_notify_segment_alignment_gate — verifies every notify segment is authored in the Unreal animation montage and aligned to integer frame boundaries.

The handoff is editor-only: the plan sets editorOnlyHandoff: true and runtimeLiveLinkPluginRequired: false, and the cooked output uses the cooked-animation-only rollback policy. V2 runtime Live Link plugins stay disabled in shipped builds — the Live Link path is used at author/cook time only, and gameplay runs against the cooked, frame-snapped AnimSequence. This mirrors the Bellona-side contract (BELLONA-MOCAP-V2-FRAME-SNAP-001, Bellona spec §17.2) and the engine configuration that keeps the runtime Live Link plugin off.

@v2/aja-consent-nil-ledger (apps/v2/aja-consent-nil-ledger/src/consent-nil-ledger.ts) records per-performer capture consent and ties it to the Themis name/image/likeness (NIL) ledger so that no captured likeness is processed without an active, revocable rights grant. It depends on the real @aja/consent-management library (ConsentManager, WithdrawalManager) and references the forthcoming @themis/likeness ledger by event contract only — it does not take a build dependency on it.

grantPerformerConsent records consent through @aja/consent-management, stamps an immutable rightsManifestSha256, captures consentChainRefs[] linking the Aja consent records to the Themis likeness license, and installs a rights gate (requiresActiveAjaConsent, requiresThemisNilLedgerStamp, blocksCaptureIngest, blocksBellonaCook). A grant is rejected if its rights manifest is not a stable sha256: digest or if a required capture purpose (studio-mocap, face-capture, voice-capture, ai-derivative-retarget) is not consented.

revokePerformerConsent runs a WithdrawalManager withdrawal and emits a cook-blocking revocation cascade. The service emits a v2.mocap.performer_consent.revoked event, links it to the upstream themis.license.revoked event on the same likeness license, and carries the revoked consentChainRefs[] so downstream consumers can trace exactly which captures lose their rights. The cascade sets blockFutureAjaProcessing and blockBellonaCook: once consent is withdrawn, the revocation must propagate within the 15-minute cascade SLA, and any in-flight or future Bellona cook that depends on that performer's likeness is blocked. The capture-rights-gate-only rollback policy keeps this gate off the deterministic gameplay path — it gates ingest and cook, not rollback simulation.


12. Configuration#

The following environment variables and configuration objects control each application's runtime behavior.

12.1 Motion Pipeline Service#

apps/aja/svc-motion-pipeline/src/server.ts reads the following environment variables:

Variable Purpose Default
PORT Service listen port 8090
HOST Service bind host 0.0.0.0
MAX_CONCURRENT_JOBS Max concurrent jobs in the job manager 10
DEFAULT_JOB_TIMEOUT_MS Default per-job timeout (ms) 1800000 (30 min)
SERVICE_NAME Logical service name motion-pipeline

The service declares @oshun/cache, @oshun/database, @oshun/event-bus, and @oshun/queue as dependencies in package.json. In app.ts the storage provider is pluggable via MotionPipelineServiceOptions.storageProvider; when none is supplied a built-in mock provider is used.

12.2 Motion AI Service#

apps/aja/svc-motion-ai/src/server.ts reads PORT (default 3040).

12.3 CLI#

apps/aja/cli/src/index.ts reads AJA_API_URL (default http://localhost:3000) and AJA_API_KEY, and loads a .env file via dotenv.

12.4 SDK#

@aja/motion-pipeline-sdk takes all configuration through the SDKConfig object passed to MotionPipelineClient; it does not itself read environment variables. DEFAULT_SDK_CONFIG supplies the default timeout and retry policy.


13. CLI#

apps/aja/cli publishes the binaries aja and aja-motion (bin in package.json), built with Commander. Source: apps/aja/cli/src/. The CLI depends on @aja/motion-pipeline-sdk, @aja/motion-formats, @aja/motion-quality, and @aja/motion-validation.

Global options (available on all subcommands): -v, --version, -q, --quiet, --no-color, --json, --api-url <url>, --api-key <key>.

Seven subcommands are registered (commands/index.ts), each implemented in a dedicated source file:

Command Source Purpose
aja process process.ts Process a video / mocap file
aja convert convert.ts Convert animation files between formats
aja inspect inspect.ts Inspect a motion file's metadata/quality
aja debug debug.ts Run a pipeline stage with diagnostics
aja config config.ts Manage named pipeline configuration profiles
aja jobs jobs.ts List/monitor/manage submitted jobs
aja health health.ts Check pipeline service health

CLI utilities under cli/src/utils/ provide shared building blocks: client.ts builds the service client, output.ts handles formatted output (including --json mode), and progress.ts renders progress indicators.


14. Validation Rules and Invariants#

These rules define the correctness constraints that the Aja domain enforces at runtime. A change that violates any of these invariants is incorrect.

  • Pipeline configuration validation — created configs are validated with validatePipelineConfiguration before being saved; invalid configs return HTTP 400 INVALID_CONFIG with an errors array. YAML configs additionally fail with YAML_PARSE_ERROR on a malformed body.
  • YAML schemaparsePipelineDefinition enforces the PipelineDefinition shape (apiVersion, kind: 'PipelineDefinition', metadata, spec). The spec quality preset must be one of draft, standard, high, ultra; job priority (when present) must be one of the five JobPriority values.
  • Job lifecycle invariants — a job's outputs can be listed only when its status is completed (JOB_NOT_COMPLETE otherwise); an SSE stream can be opened only when status is pending, queued, or running (JOB_NOT_ACTIVE otherwise).
  • V2-deferred stages — if a pipeline config enables pose-estimation, skeleton-fitting, domain-analysis, or retargeting, the processor marks the stage 'skipped' (it does not fail the job) and logs the V1-P2-0331 deferral.
  • Embodied-instruction request validation — the demonstration-plan, coaching-overlay, and session-handoff endpoints reject malformed bodies with HTTP 400 and Zod-flattened issues.
  • Webhook progress throttling — webhook job.progress deliveries fire only at progress percentages 25, 50, and 75.
  • SDK retry policy — the SDK retries only retryable errors on the configured retryable status codes, with exponential backoff plus jitter.
  • Format scale conventions — C3D and TRC import default to scaleFactor: 0.001 (millimetres to metres); USD export defaults to metersPerUnit: 0.01 (centimetres).
  • Skeleton parent invariant — in SkeletonJoint, parentIndex of -1 denotes the root joint; STANDARD_JOINT_NAMES and SKELETON_PARENT_INDICES arrays must be index-aligned per skeleton type — index i in the names array must correspond to index i in the parent-indices array.

15. Technology Stack#

The table below covers the key technology choices for each layer of the Aja domain.

Layer Technology
Language TypeScript (Node.js); Python for the aja-motion-pipeline SDK
Pipeline service Fastify, via @lilith/fastify-core (port 8090)
AI service Hono + @hono/node-server (port 3040)
CLI Commander, with chalk, inquirer, ora, dotenv, glob
Validation Zod (AI service request validation; pipeline YAML/config)
Job progress delivery Server-Sent Events (not WebSocket)
YAML js-yaml (pipeline definitions)
Motion format I/O Custom TypeScript parsers in @aja/motion-formats
Build tsc per project (nx for the CLI build script)
Testing Vitest (TypeScript); pytest tooling for the Python SDK
Shared infrastructure @oshun/cache, @oshun/database, @oshun/event-bus, @oshun/queue, @oshun/config, @oshun/logging, @oshun/errors, @oshun/health (declared as deps)

16. Acceptance Criteria#

A change to the Aja domain is acceptable when all of the following conditions hold:

  1. Type integrity — all motion data flows through AnimationClip / SkeletonDefinition from @aja/motion-formats; new skeleton types are added to the relevant StandardSkeletonType union and to STANDARD_JOINT_NAMES / SKELETON_PARENT_INDICES index-aligned.
  2. Pipeline-stage discipline — new pipeline stages are added to the PipelineStage union; ML-only stages remain in V2_DEFERRED_STAGES until a real implementation lands, and the processor skips (never fakes) deferred stages.
  3. API consistency — pipeline routes stay under the /v1/pipeline/... prefix and return the documented error-body shape { code, message }; AI routes stay under /api/v1/....
  4. Validation — request bodies that have a Zod schema (the embodied-instruction endpoints) or a config validator are rejected with HTTP 400 and structured issues on malformed input.
  5. Event coherence — job lifecycle changes emit the corresponding JobEvent; webhook and SSE deliveries derive from those events; webhook job.progress stays throttled to 25/50/75 %.
  6. Build and test — every changed library and app type-checks (npx tsc --noEmit) and its Vitest suite passes; the Python SDK's tooling passes for motion-pipeline-sdk-python.
  7. No fabricated capability — documentation and code claims are limited to what exists under libs/aja/* and apps/aja/*.