Screenflow Service - Infrastructure
Infrastructure setup
Databases
Uses Prisma with PostgreSQL.
Schema Location: services/screenflow/prisma/schema.prisma
docker compose -f 'docker-compose.yml' up -d --build 'databases'to stop:
docker compose -f 'docker-compose.yml' down 'databases'Prisma Commands
nx run screenflow:prisma:generate # generate types
nx run screenflow:prisma:reset # reset the database depending on the variable DATABASE_URLPerforming a Migration
- Modify the
schema.prismafile of the service you're working with - Trigger the nx command
migrate:dev:create, which will automatically generate the migration file within the/migrationsfolder. Check the result as this command can be dangerous to merge/roll back - Run the migration with the nx command
migrate:dev - Generate the schemas with the nx command
prisma:generate. - Obviously test your changes
You can then create your PR.
For deployment to the environments, you'll notice in the Dockerfile that a script entrypoint.sh is triggered. This script will automatically deploy your migration, meaning your migration will automatically be applied across the environments once the service starts.
Valkey (queues & event system)
docker compose -f 'docker-compose.yml' up -d --build 'valkey'to stop:
docker compose -f 'docker-compose.yml' down 'valkey'Architecture & Connections
The ScreenFlow service acts as an intermediary layer between the frontend, Guacamole service, and remote Windows instances. The following diagram details all connections:
Connection Flow Diagram
graph TB
subgraph Frontend["Frontend (Browser)"]
FE_HTTP[HTTP/REST API Client]
FE_WS[WebSocket Client<br/>RDP Tunnel]
FE_SOCKET[Socket.IO Client<br/>Events]
end
subgraph ScreenFlow["ScreenFlow Service"]
SF_API[REST API<br/>Controllers]
SF_WS_GATEWAY[WebSocket Gateway<br/>/ws/guacamole-tunnel/:id]
SF_WS_PROXY[WebSocket Proxy Service]
SF_SOCKET[Socket.IO Gateway<br/>Events]
SF_GUAC_CLIENT[Guacamole API Client]
end
subgraph Guacamole["Guacamole Service"]
GUAC_API[REST API<br/>/guacamole/api]
GUAC_WS[WebSocket Tunnel<br/>/websocket-tunnel]
GUACD[guacd Daemon<br/>RDP Handler]
GUAC_DB[(Guacamole DB)]
end
subgraph Instances["Windows Instances (EC2)"]
INST_RDP[RDP Server<br/>Port 3389]
INST_API[Windows Instance API<br/>Port 8000]
end
%% Frontend to ScreenFlow
FE_HTTP -->|HTTPS<br/>REST API| SF_API
FE_WS -->|WSS<br/>guacamole subprotocol| SF_WS_GATEWAY
FE_SOCKET -->|WSS<br/>Socket.IO| SF_SOCKET
%% ScreenFlow to Guacamole
SF_API -->|HTTP/HTTPS<br/>Connection Management| GUAC_API
SF_WS_PROXY -->|WSS<br/>guacamole subprotocol| GUAC_WS
SF_GUAC_CLIENT -->|HTTP/HTTPS<br/>API Calls| GUAC_API
SF_WS_GATEWAY --> SF_WS_PROXY
%% Guacamole to Instances
GUAC_WS --> GUACD
GUACD -->|RDP<br/>TCP:3389| INST_RDP
GUAC_API --> GUAC_DB
%% ScreenFlow to Instances
SF_API -->|HTTP/HTTPS<br/>Screenshots/Scripts| INST_API
%% Styling
classDef frontend fill:#e1f5ff,stroke:#01579b,stroke-width:2px
classDef screenflow fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef guacamole fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef instances fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
classDef database fill:#fce4ec,stroke:#880e4f,stroke-width:2px
class FE_HTTP,FE_WS,FE_SOCKET frontend
class SF_API,SF_WS_GATEWAY,SF_WS_PROXY,SF_SOCKET,SF_GUAC_CLIENT screenflow
class GUAC_API,GUAC_WS,GUACD guacamole
class INST_RDP,INST_API instances
class GUAC_DB databaseDetailed Connections
1. Frontend → ScreenFlow
HTTP/REST API (HTTPS)
- Protocol: HTTP/HTTPS
- Purpose: All REST API calls for managing instances, workflows, tasks, attempts, and commands
- Client:
typedScreenflowClient(generated from OpenAPI spec) andscreenflowClient(Axios-based) - Base URL: Configured via
SCREENFLOW_API_DOMAINenvironment variable - Authentication: Cookie-based (credentials: 'include')
- Endpoints: All CRUD operations for screenflow resources (instances, workflows, runs, tasks, attempts, commands, etc.)
WebSocket (WSS) - RDP Tunnel
- Protocol: WebSocket Secure (WSS) with 'guacamole' subprotocol
- Purpose: Real-time RDP connection tunnel for remote desktop access
- Endpoint:
/ws/guacamole-tunnel/{instanceId} - Client:
GuacamoleWebSocketServicein frontend - Implementation: Uses Guacamole JavaScript client library to establish WebSocket connection
- Flow: Frontend connects to ScreenFlow WebSocket gateway, which proxies to Guacamole
Socket.IO (WSS)
- Protocol: Socket.IO over WebSocket Secure
- Purpose: Real-time event notifications (command recorded, command created)
- Client:
screenflowSocket(Socket.IO client) - Events:
COMMAND_RECORDED: Broadcasts when a command is recorded during RDP sessionCOMMAND_CREATED: Broadcasts when a new command is created
- Authentication: Token-based authentication with expiration handling
2. ScreenFlow → Guacamole
HTTP REST API (HTTP/HTTPS)
- Protocol: HTTP/HTTPS
- Purpose: Guacamole API operations (authentication, connection management)
- Client:
GuacamoleApiClientService(generated OpenAPI client) - Base URL: Configured via
GUACAMOLE_URLenvironment variable (e.g.,http://localhost:8080/guacamole) - Operations:
generateToken: Authenticate and get admin auth tokencreateConnection: Create RDP connection configuration in GuacamolegetConnections: List connections by instance IDgetConnectionDetails: Get specific connection detailsdeleteConnection: Remove connection configuration
- Authentication: Admin username/password stored in config (
guacamoleAdminUsername,guacamoleAdminPassword) - Connection Parameters: When creating connections, ScreenFlow provides:
hostname: Private IP address of the EC2 instanceport: 3389 (standard RDP port)username: Screenflow instance username (from config)password: Screenflow instance password (from config)protocol: RDP- Various RDP-specific parameters (security, display settings, etc.)
WebSocket (WSS) - RDP Tunnel Proxy
- Protocol: WebSocket Secure (WSS) with 'guacamole' subprotocol
- Purpose: Proxy RDP traffic between frontend and Guacamole
- Endpoint:
{GUACAMOLE_URL}/websocket-tunnel?token={token}&GUAC_DATA_SOURCE={dataSource}&GUAC_ID={connectionId}&GUAC_TYPE=c - Implementation:
GuacamoleWebSocketProxyServicecreates bidirectional proxy - Flow:
- Frontend connects to ScreenFlow at
/ws/guacamole-tunnel/{instanceId} - ScreenFlow looks up Guacamole connection by instanceId
- ScreenFlow authenticates with Guacamole and gets connection ID
- ScreenFlow establishes WebSocket connection to Guacamole's websocket-tunnel endpoint
- ScreenFlow proxies all messages bidirectionally between frontend and Guacamole
- ScreenFlow also parses and records input commands for analytics
- Frontend connects to ScreenFlow at
3. Guacamole → Instances
RDP Protocol (TCP)
- Protocol: Remote Desktop Protocol (RDP) over TCP
- Port: 3389 (standard RDP port)
- Purpose: Direct RDP connection to Windows EC2 instances
- Connection Details:
- Hostname: Private IP address of the EC2 instance (retrieved from resource repository)
- Port: 3389
- Credentials: Screenflow instance username/password (configured in ScreenFlow service)
- Security: 'any' (accepts any RDP security method)
- Certificate Validation: Disabled ('ignore-cert': 'true')
- Implementation: Guacamole uses its
guacddaemon to establish and manage RDP connections - Connection Lifecycle:
- ScreenFlow creates connection configuration in Guacamole via API
- When frontend requests RDP session, Guacamole uses this configuration to connect to the instance
- Guacamole manages the RDP session lifecycle
Database (Guacamole DB)
- Protocol: Database connection (PostgreSQL/MySQL)
- Purpose: Guacamole stores connection configurations, user sessions, and connection history
- Access: Guacamole service manages its own database connections
4. ScreenFlow → Instances (Windows Instance Service)
HTTP API (HTTP/HTTPS)
- Protocol: HTTP/HTTPS
- Purpose: Screenshot functionality and script execution on Windows instances
- Host: Configured via
WINDOWS_INSTANCE_HOST(local dev) or direct private IP (production) - Port: Configured via
WINDOWS_INSTANCE_PORT(default: 8000) - Authentication: API key via
WINDOWS_INSTANCE_API_KEY - Usage: Used for taking screenshots and running scripts on Windows instances outside of RDP sessions
Connection Sequence for RDP Session
Frontend initiates connection:
- Frontend calls ScreenFlow REST API to ensure Guacamole connection exists (or creates it)
- Frontend establishes WebSocket connection to ScreenFlow:
wss://screenflow.domain/ws/guacamole-tunnel/{instanceId}
ScreenFlow proxies to Guacamole:
- ScreenFlow looks up Guacamole connection configuration by instanceId
- ScreenFlow authenticates with Guacamole API to get admin token
- ScreenFlow establishes WebSocket connection to Guacamole:
wss://guacamole/websocket-tunnel?token=...&GUAC_ID={connectionId}
Guacamole connects to instance:
- Guacamole uses stored connection configuration (hostname, port 3389, credentials)
- Guacamole's guacd daemon establishes RDP connection to Windows instance
- RDP session is established on port 3389
Data flow:
- RDP display updates flow: Instance → Guacamole → ScreenFlow → Frontend
- User input flows: Frontend → ScreenFlow → Guacamole → Instance
- ScreenFlow intercepts and records input commands for analytics
Security Considerations
- RDP Connections: Use private IP addresses within VPC, not exposed publicly
- WebSocket Tunnels: All WebSocket connections use WSS (secure) in production
- Authentication:
- Frontend uses cookie-based authentication for REST API
- ScreenFlow uses admin credentials for Guacamole API
- Guacamole uses configured instance credentials for RDP
- Network Isolation: All connections between services occur within AWS VPC in production
Local Development Setup
This section covers the local development setup for the screenflow service. Where we connect through bastions to:
- screenflow and guacamole staging databases.
- guacamole staging service.
- windows instance.
Start services locally
nx run coding:dev(for auth and tenant)nx run screenflow:devnx run frontend:dev
Bastion connection scripts
Use the connection scripts from various guides and add them following to your .zshrc file (don't forget to source it afterwards: source ~/.zshrc).
Hint: You can reuse the same _PARALLEL_CONFIG for all scripts and paste the rest of the scripts in the same file.
connect-screenflow-db-staging-> port 65438 (screenflow db) - see database accessconnect-guacamole-db-staging-> port 65437 (guacamole db) - see database accessconnect-guacamole-staging-> port 8080 (guacamole service) - see aws service task connection
Hint: test you connection by going to http://localhost:8080/guacamole/ and logging in with the .env credentials. Or by running curl -X POST "http://localhost:8080/guacamole/api/tokens" -H "Content-Type: application/x-www-form-urlencoded" -d "username=guacadmin&password=<guacamole_admin_password>"
connect-custom-ec2 staging <instance-id> port 8000(for windows instance) - see ec2 windows connection
Hint: test you connection by running curl -X GET "http://localhost:8000/computer/display/dimensions" -H "x-api-key: placeholder_key".
ZSHRC Configuration example (refer to the linked pages for the most up to date scripts)
# ...your personal zshrc configs
# Configuration
typeset -A _PARALLEL_CONFIG
_PARALLEL_CONFIG=(
# Bastion instances
[bastion_prod]="i-0430e03b4e1c1bbdd"
[bastion_staging]="i-0f052721e4ba2c318"
# RDS cluster identifiers
[rds_suffix_prod]="clwscu6m8xeq.eu-west-3.rds.amazonaws.com"
[rds_suffix_staging]="c5wmseg8am3n.eu-west-3.rds.amazonaws.com"
# Environment prefixes
[prefix_prod]="parallel-euw3-prod"
[prefix_staging]="parallel-euw3-staging"
# Default local port ranges (prod: 554xx, staging: 654xx)
[port_base_prod]="55432"
[port_base_staging]="65432"
)
# ============================================
# Database Connections (via Bastion)
# ============================================
# Database port offsets
typeset -A _DB_PORT_OFFSET
_DB_PORT_OFFSET=(
[coding]=0
[valuation]=1
[codingsnapshot]=2
[dashboard]=3
[screenflow]=4
[guacamole]=5
)
# Generic function to connect to any database
_connect-db() {
local ENV="$1"
local DB_NAME="$2"
local CUSTOM_PORT="$3"
aws-sso-util login --profile "parallel_${ENV}"
export AWS_PROFILE="parallel_${ENV}"
local BASTION="${_PARALLEL_CONFIG[bastion_${ENV}]}"
local RDS_SUFFIX="${_PARALLEL_CONFIG[rds_suffix_${ENV}]}"
local PREFIX="${_PARALLEL_CONFIG[prefix_${ENV}]}"
local PORT_BASE="${_PARALLEL_CONFIG[port_base_${ENV}]}"
local PORT_OFFSET="${_DB_PORT_OFFSET[${DB_NAME}]:-0}"
local HOST="${PREFIX}-${DB_NAME}-db.${RDS_SUFFIX}"
local LOCAL_PORT="${CUSTOM_PORT:-$((PORT_BASE + PORT_OFFSET))}"
echo ""
echo "🗄️ Connecting to ${DB_NAME} database (${ENV})..."
echo " Host: ${HOST}"
echo " Port: 5432 → localhost:${LOCAL_PORT}"
echo ""
aws ssm start-session \
--target "$BASTION" \
--document-name "AWS-StartPortForwardingSessionToRemoteHost" \
--parameters "{\"host\":[\"${HOST}\"],\"portNumber\":[\"5432\"],\"localPortNumber\":[\"${LOCAL_PORT}\"]}"
}
# ================== PROD ==================
connect-coding-db-prod() { _connect-db "prod" "coding" "$1"; }
connect-valuation-db-prod() { _connect-db "prod" "valuation" "$1"; }
connect-codingsnapshot-db-prod() { _connect-db "prod" "codingsnapshot" "$1"; }
connect-dashboard-db-prod() { _connect-db "prod" "dashboard" "$1"; }
connect-screenflow-db-prod() { _connect-db "prod" "screenflow" "$1"; }
connect-guacamole-db-prod() { _connect-db "prod" "guacamole" "$1"; }
# ================ STAGING =================
connect-coding-db-staging() { _connect-db "staging" "coding" "$1"; }
connect-valuation-db-staging() { _connect-db "staging" "valuation" "$1"; }
connect-codingsnapshot-db-staging() { _connect-db "staging" "codingsnapshot" "$1"; }
connect-dashboard-db-staging() { _connect-db "staging" "dashboard" "$1"; }
connect-screenflow-db-staging() { _connect-db "staging" "screenflow" "$1"; }
connect-guacamole-db-staging() { _connect-db "staging" "guacamole" "$1"; }
# ============================================
# Connect to Specific EC2 (via Bastion)
# ============================================
_connect-ec2-instance() {
local ENV="$1"
local INSTANCE_ID="$2"
local LOCAL_PORT="$3"
local REMOTE_PORT="${4:-80}" # Default to port 80 if not specified
# 1. Login & Set Context
aws-sso-util login --profile "parallel_${ENV}"
export AWS_PROFILE="parallel_${ENV}"
local BASTION="${_PARALLEL_CONFIG[bastion_${ENV}]}"
# 2. Get the Private IP of the specific EC2 Instance
echo "🔍 Resolving IP for Instance ID: ${INSTANCE_ID}..."
local PRIVATE_IP
PRIVATE_IP=$(aws ec2 describe-instances \
--instance-ids "$INSTANCE_ID" \
--query 'Reservations[0].Instances[0].PrivateIpAddress' \
--output text)
if [[ "$PRIVATE_IP" == "None" ]] || [[ -z "$PRIVATE_IP" ]]; then
echo "❌ Could not find Private IP for instance: $INSTANCE_ID"
echo " (Check if the instance ID is correct and the profile has permission to view it)"
return 1
fi
# 3. Connect via SSM (Tunneling through Bastion)
echo ""
echo "🔗 Connecting to EC2 Instance (${ENV})..."
echo " Instance: $INSTANCE_ID"
echo " Target IP: $PRIVATE_IP"
echo " Via Bastion: $BASTION"
echo ""
echo "📍 Open: http://localhost:${LOCAL_PORT}/"
echo ""
aws ssm start-session \
--target "$BASTION" \
--document-name "AWS-StartPortForwardingSessionToRemoteHost" \
--parameters "{\"host\":[\"${PRIVATE_IP}\"],\"portNumber\":[\"${REMOTE_PORT}\"],\"localPortNumber\":[\"${LOCAL_PORT}\"]}"
}
connect-custom-ec2() {
local ENV="$1"
local INSTANCE_ID="$2"
local REMOTE_PORT="$3"
local LOCAL_PORT="${4:-$REMOTE_PORT}" # Defaults local port to match remote port if not set
if [[ -z "$ENV" || -z "$INSTANCE_ID" || -z "$REMOTE_PORT" ]]; then
echo "❌ Usage: connect-custom-ec2 <env> <instance-id> <remote-port> [local-port]"
echo " Ex: connect-custom-ec2 staging i-0b31cb0b11ed4036e 8000"
return 1
fi
_connect-ec2-instance "$ENV" "$INSTANCE_ID" "$LOCAL_PORT" "$REMOTE_PORT"
}
# ============================================
# Connect to Generic Task Service
# ============================================
_connect-service() {
local ENV="$1"
local SERVICE="$2"
local LOCAL_PORT="$3"
local REMOTE_PORT="$4"
aws-sso-util login --profile "parallel_${ENV}"
export AWS_PROFILE="parallel_${ENV}"
local BASTION="${_PARALLEL_CONFIG[bastion_${ENV}]}"
local PREFIX="${_PARALLEL_CONFIG[prefix_${ENV}]}"
local CLUSTER="${PREFIX}-ecs-cluster"
local ECS_SERVICE="${PREFIX}-ecs-${SERVICE}"
echo "🔍 Finding task for ${SERVICE}..."
# 1. Get Task ARN
local TASK_ARN
TASK_ARN=$(aws ecs list-tasks \
--cluster "$CLUSTER" \
--service-name "$ECS_SERVICE" \
--query 'taskArns[0]' \
--output text)
if [[ "$TASK_ARN" == "None" ]] || [[ -z "$TASK_ARN" ]]; then
echo "❌ No running task found for service: $SERVICE"
return 1
fi
local HOST_IP=""
# 2. Get Task Details (Raw JSON) to find ENI
# We grep for the specific pattern "eni-..." inside the task description
local TASK_JSON
TASK_JSON=$(aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK_ARN" --output json)
# Extract ENI ID using grep (looks for "value": "eni-...")
local ENI_ID
ENI_ID=$(echo "$TASK_JSON" | grep -o '"value": "eni-[^"]*"' | cut -d'"' -f4 | head -n 1)
if [[ -n "$ENI_ID" ]]; then
HOST_IP=$(aws ec2 describe-network-interfaces \
--network-interface-ids "$ENI_ID" \
--query 'NetworkInterfaces[0].PrivateIpAddress' \
--output text)
fi
# 3. Fallback: Check for EC2 Instance ID (if grep failed or it's not Fargate)
if [[ -z "$HOST_IP" || "$HOST_IP" == "None" ]]; then
# Extract Instance ID using grep
local INSTANCE_ID
INSTANCE_ID=$(echo "$TASK_JSON" | grep -o '"ec2InstanceId": "i-[^"]*"' | cut -d'"' -f4 | head -n 1)
if [[ -n "$INSTANCE_ID" ]]; then
HOST_IP=$(aws ec2 describe-instances \
--instance-ids "$INSTANCE_ID" \
--query 'Reservations[0].Instances[0].PrivateIpAddress' \
--output text)
fi
fi
if [[ -z "$HOST_IP" || "$HOST_IP" == "None" ]]; then
echo "❌ Could not resolve Host IP. The task might be pending or the network mode is unsupported."
return 1
fi
echo ""
echo "🥑 Connecting to ${SERVICE} service (${ENV})..."
echo " Task: $TASK_ARN"
echo " Host: $HOST_IP"
echo " Port: ${REMOTE_PORT} → localhost:${LOCAL_PORT}"
echo ""
aws ssm start-session \
--target "$BASTION" \
--document-name "AWS-StartPortForwardingSessionToRemoteHost" \
--parameters "{\"host\":[\"${HOST_IP}\"],\"portNumber\":[\"${REMOTE_PORT}\"],\"localPortNumber\":[\"${LOCAL_PORT}\"]}"
}
# ================== PROD ==================
# Prod usually starts at 9080.
connect-guacamole-prod() { _connect-service "prod" "guacamole" "8080" "9080"; }
# ================ STAGING =================
# Staging usually starts at 8080.
connect-guacamole-staging() { _connect-service "staging" "guacamole" "8080" "8080"; }Environment Configuration
Make sure the following environment variables are defined in your screenflow .env.dev file:
# have the env variables point to staging dbs
# beware of the ?s in the password, they need to be encoded.
DATABASE_URL=postgresql://screenflow:<staging_db_pw>@localhost:65436/screenflow?schema=public
GUACAMOLE_DATABASE_URL=postgresql://guacamole:<staging_guacamole_db_pw>@localhost:65437/guacamole?schema=public
DATABASE_USERNAME=screenflow
DATABASE_PASSWORD="<staging_db_pw>"
DATABASE_ADDRESS=localhost
DATABASE_PORT=65436
DATABASE_NAME=screenflow
GUACAMOLE_DATABASE_USERNAME=guacamole
GUACAMOLE_DATABASE_PASSWORD="<staging_guacamole_db_pw>"
GUACAMOLE_DATABASE_ADDRESS=localhost
GUACAMOLE_DATABASE_PORT=65437
GUACAMOLE_DATABASE_NAME=guacamole
GUACAMOLE_URL=http://localhost:8080/guacamole
WINDOWS_INSTANCE_HOST="http://localhost:8000"Troubleshooting
- Password Encoding: If you encounter the following error, it means the database URL is not properly encoded. It's likely due to the database password containing special characters that need to be escaped in the url.
prisma:error Cannot read properties of undefined (reading 'searchParams')- Disable SSL: If you encounter the following error, check if your db url contains
&disable_ssl=true. Remove it.
Invalid `this.screenflowPrismaService.defaultInstance.instance.count()` invocation in
/Users/eliott/Desktop/parallel/alphacode/services/screenflow/src/infrastructure-layer/prisma/screenflow/repositories/entities/instance/prisma-instance.repository.ts:41:76
38 async count(options?: InstanceRepositoryOptions): Promise<number> {
39 const { tenantId } = options ?? {}
40
→ 41 return await this.screenflowPrismaService.defaultInstance.instance.count(
User was denied access on the database `screenflow`Local Development Setup - other commands
Port Forwarding to Guacamole
To run screenflow locally, you must first establish a port forwarding session to the Guacamole ECS container.
Note: The instance ID must be fetched from AWS. Use the EC2 instance named *-bastion-*.
aws ssm start-session --target "i-04bc51048b5fcfcda" --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"host":["staging-guacamole.internal"],"portNumber":["8080"], "localPortNumber":["8080"]}'Replace i-04bc51048b5fcfcda with the current bastion instance ID from AWS.
Port Forwarding to Window Instance
When working locally with window instances (for screenshot functionality), you need to establish a port forwarding session to the EC2 instance running the window instance service.
Local Development Setup
- Set the following environment variables in your
.env.devfile:
WINDOWS_INSTANCE_HOST=local.beparallel.com:8000
WINDOWS_INSTANCE_PORT=8000
WINDOWS_INSTANCE_API_KEY=your-api-key- Start the SSM port forwarding session:
aws ssm start-session \
--target i-0dacabcb36c8bfd2a \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["8000"],"localPortNumber":["8000"]}' \
--region eu-west-3 \
--profile parallel_stagingNote: Replace the instance ID (i-0dacabcb36c8bfd2a) with the current window instance EC2 ID from AWS if it has changed.
How It Works
- Local Development: The screenflow service uses
WINDOWS_INSTANCE_HOST(e.g.,local.beparallel.com:8000) to connect through the SSM tunnel to the window instance running on AWS. - Deployed on AWS: When deployed, the service uses the private IP address of the window instance directly for internal VPC communication. The
WINDOWS_INSTANCE_HOSTenvironment variable is not set in production.
Running the Service
Install Dependencies
pnpm installEnvironment variables
- Add a
.env.devat the service root - Get the environment variables from 1password for the corresponding service
Commands
# Development
nx run screenflow:dev
# Build
nx run screenflow:build
# Test
nx run screenflow:test
# Lint
nx run screenflow:lint
# Run in Docker
docker compose -f 'docker-compose.yml' up -d --build 'screenflow'
# Check dependencies
nx show project screenflow
# Show affected projects (based on git changes)
nx affected:graphShared Packages
The shared packages (dto, models, utils) are automatically linked and can be imported in both backend and frontend applications. Any changes to these packages will trigger rebuilds in dependent applications.
These packages mostly contain global functions (like logger, utils, ...) shared among services, providing common functionality and ensuring consistency across the monorepo.
Available Packages
- dto - Data Transfer Objects
- models - Shared data models
- utils - Utility functions and helpers