PhoenixKitSync

Peer-to-peer data sync module for PhoenixKit.

Provides bidirectional data synchronization between PhoenixKit instances — sync between dev and prod, dev and dev, or different websites entirely.

Installation

Add to your parent app's mix.exs:

{:phoenix_kit_sync, "~> 0.1.0"}

The module is auto-discovered via PhoenixKit's beam scanning — no additional configuration needed. Enable it from the admin dashboard under Modules.

Architecture

Automatic Cross-Site Registration

The module uses permanent connections with automatic cross-site registration:

  1. Sender creates a connection pointing to a remote site's URL
  2. System automatically notifies the remote site via API
  3. Remote site registers the connection based on their incoming settings:
    • Auto Accept: Connection activates immediately
    • Require Approval: Connection appears as pending
    • Require Password: Only accepts with correct password
    • Deny All: Rejects the connection request
  4. Both sites have matching connection records for data sync
  5. All transfers are tracked in the history with full audit trail

Connection Types

When you create a sender connection, the remote site automatically receives a corresponding receiver connection.

Features

Connection Settings

Sender-Side Controls

Setting Description
Approval Modeauto_approve, require_approval, or per_table
Allowed Tables Whitelist of tables the receiver can access
Excluded Tables Blacklist of tables to hide from receiver
Auto-Approve Tables Tables that don't need approval (when mode is per_table)
Max Downloads Limit total number of transfer sessions
Max Records Total Limit total records that can be downloaded
Max Records Per Request Limit records per single request (default: 10,000)
Rate Limit Requests per minute limit (default: 60)
Download Password Optional password required for each transfer
IP Whitelist Only allow connections from specific IPs
Allowed Hours Time-of-day restrictions (e.g., only 2am-5am)
Expiration Date Auto-expire the connection after a date

Connection Statuses

Status Description
Pending Just created, awaiting activation
Active Ready to accept connections
Suspended Temporarily disabled (can be reactivated)
Revoked Permanently disabled
Expired Auto-expired due to limits or date

Incoming Connection Settings

Control how your site handles connection requests from other sites:

Mode Behavior
Auto Accept Incoming connections activate immediately
Require Approval Connections appear as pending, need manual approval
Require Password Sender must provide correct password
Deny All Reject all incoming connection requests

Workflow

Setting Up a Sender Connection

  1. Navigate to the Sync connections page in the admin dashboard
  2. Click "New Connection"
  3. Enter a name and the remote site's URL
  4. Configure access controls (approval mode, tables, limits)
  5. Save — the connection is created and token generated
  6. The remote site is notified automatically!
    • If successful, the connection appears in their list
    • Based on their settings, it may be auto-approved or pending
  7. If notification fails, share the token manually as a fallback

What Happens on the Remote Site

When you create a sender connection:

Programmatic API

Connection Management

alias PhoenixKitSync.Connections

# Create a sender connection
{:ok, connection} = Connections.create_connection(%{
  name: "Production Backup",
  direction: "sender",
  site_url: "https://backup.example.com",
  approval_mode: "auto_approve",
  allowed_tables: ["users", "posts"],
  max_downloads: 100,
  created_by_uuid: current_user.uuid
})

# The token is returned in connection.auth_token (only on create)
token = connection.auth_token

# Approve a pending connection
{:ok, connection} = Connections.approve_connection(connection, admin_user_uuid)

# Suspend a connection
{:ok, connection} = Connections.suspend_connection(connection, admin_user_uuid, "Security audit")

# Reactivate a suspended connection
{:ok, connection} = Connections.reactivate_connection(connection)

# Revoke permanently
{:ok, connection} = Connections.revoke_connection(connection, admin_user_uuid, "No longer needed")

# Validate a token (used by receiver when connecting)
case Connections.validate_connection(token, client_ip) do
  {:ok, connection} -> # Token is valid, connection is active
  {:error, :invalid_token} -> # Token doesn't exist
  {:error, :connection_expired} -> # Expired or revoked
  {:error, :download_limit_reached} -> # Max downloads reached
  {:error, :ip_not_allowed} -> # IP not in whitelist
  {:error, :outside_allowed_hours} -> # Outside time window
end

Transfer Tracking

alias PhoenixKitSync.Transfers

# Record a transfer
{:ok, transfer} = Transfers.create_transfer(%{
  direction: "send",
  connection_uuid: connection.uuid,
  table_name: "users",
  records_transferred: 150,
  bytes_transferred: 45000,
  status: "completed"
})

# Get transfer history
transfers = Transfers.list_transfers(
  connection_uuid: connection.uuid,
  direction: "send",
  status: "completed"
)

# Get statistics for a connection
stats = Transfers.connection_stats(connection.uuid)
# => %{total_transfers: 25, total_records: 5000, total_bytes: 1500000}

System Control

# Enable/disable
PhoenixKitSync.enabled?()
PhoenixKitSync.enable_system()
PhoenixKitSync.disable_system()
PhoenixKitSync.get_config()

# Local database inspection
{:ok, tables} = PhoenixKitSync.list_tables()
{:ok, schema} = PhoenixKitSync.get_schema("users")
{:ok, records} = PhoenixKitSync.export_records("users", limit: 100)

# Import with conflict strategy
{:ok, result} = PhoenixKitSync.import_records("users", records, :skip)

Remote Client

alias PhoenixKitSync.Client

{:ok, client} = Client.connect("https://sender.com", "ABC12345")
{:ok, tables} = Client.list_tables(client)
{:ok, result} = Client.transfer(client, "users", strategy: :skip)
Client.disconnect(client)

API Endpoints

Cross-site communication endpoints (under the configured URL prefix):

Database

Table migrations are currently managed by PhoenixKit's core migration system. If the tables don't already exist, this package can create them automatically via PhoenixKitSync.Migration.up/0.

See docs/table_structure.md in the source repository for full schema documentation.

Future Plans

Auto-Sync Scheduling (Planned)

Connections have fields for auto-sync but the scheduler isn't implemented yet:

Security Considerations

Troubleshooting

Connection Issues

  1. Verify the token is correct and hasn't been regenerated
  2. Check connection status is "active"
  3. Verify IP is in whitelist (if configured)
  4. Check time-of-day restrictions
  5. Verify download/record limits haven't been exceeded

Transfer Failures

  1. Check transfer history for error messages
  2. Verify table is in allowed tables (if configured)
  3. Check approval status if approval mode is enabled
  4. Review server logs for detailed errors