JobSync Documentation

repository·main·Indexed 20 days ago

https://github.com/gsync/jobsync

A self-hosted job search assistant for tracking applications, managing resumes, and automating job discovery using AI. JobSync supports local AI execution via Ollama or cloud providers (OpenAI, DeepSeek, Google Gemini, OpenRouter) and includes a Model Context Protocol (MCP) server for integration with AI agents like Claude Desktop. It can be deployed via Docker Compose and features built-in time tracking for user activities.

Tokens
19.6K
Snippets
74
Records
105
Agent score
72%

What's inside JobSync

  1. Quick Start with JobSync via Docker

    main

    To get JobSync running locally, ensure Docker is installed and running, then clone the repository and use Docker Compose to start the services.

    Note: The initial startup builds the application image, which may take several minutes. If the application is unreachable immediately after running the command, wait a few moments before accessing it in your browser.

    Once running, access the application at http://localhost:3737 and create your account. AI provider API keys can be configured in the Settings menu after signing in.

    git clone https://github.com/Gsync/jobsync.git
    cd jobsync
    docker compose up
  2. Update JobSync

    main

    To pull the latest changes and rebuild your installation, run the provided deploy script from the project directory.

    On Linux/macOS:

    curl -fsSL https://raw.githubusercontent.com/Gsync/jobsync/main/deploy.sh | sudo bash -s

    On Windows: Run the PowerShell script natively from the project directory:

    .\deploy.ps1

    Note for Homelab users: If you are hosting on a remote server, ensure you edit NEXTAUTH_URL in your .env file to use your server's IP address instead of localhost.

  3. Configure AI Model Providers in JobSync

    main

    JobSync supports multiple AI model providers for its AI-driven features. To use these features, you must configure your provider in Settings > AI Settings after signing in.

    Critical Requirement: Selected models must support structured output for AI features to function correctly.

    Supported Providers

    Ollama (Local)

    Runs models locally on your machine using Ollama.

    • Setup: Ensure Ollama is installed and running on the same system.
    • Configuration: AI settings will automatically list available models based on your local Ollama downloads.
    • Network/Homelab: If running JobSync on a server, expose Ollama to the network via Ollama settings and point the ollama base url to your local system IP in the JobSync API keys section.
    • Optimization: It is recommended to increase the Ollama context length from the default 4k to 16k to prevent truncation of long resume or job description prompts during automated job matching.
    • API Key: No API key is required.

    OpenAI

    • Setup: Obtain an API key from platform.openai.com/api-keys.
    • Configuration: Add the key in Settings > AI Settings, select OpenAI as the provider, and choose a model. Available models are fetched dynamically.

    DeepSeek

    • Setup: Obtain an API key from platform.deepseek.com/api_keys.
    • Configuration: Add the key in Settings > AI Settings and select DeepSeek as the provider.

    Google Gemini

    • Setup: Obtain an API key from aistudio.google.com/apikey.
    • Configuration: Add the key in Settings > AI Settings and select Gemini as the provider.

    OpenRouter

    • Setup: Obtain an API key from openrouter.ai/keys.
    • Configuration: Add the key in Settings > AI Settings and select OpenRouter to access models from various providers via a single API.
  4. Set up JobSync MCP Server for AI Agents

    main

    JobSync includes a built-in Model Context Protocol (MCP) server that allows AI agents (like Claude Desktop) to add job applications and Question Bank entries directly from your chat interface.

    1. Generate an Access Token

    1. Sign in to JobSync and navigate to Settings > MCP Access.
    2. Click Generate, provide a name (e.g., "Claude Desktop"), and select an expiry.
    3. Copy the generated token and the configuration snippet. The full token is only displayed once.

    2. Configure your MCP Client

    For Claude Desktop

    Claude Desktop requires mcp-remote as a bridge because it only supports local (stdio) MCP servers directly. Paste the following into your claude_desktop_config.json (found via Settings > Developer > Edit Config):

    {
      "mcpServers": {
        "jobsync": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "http://<your-jobsync-url>/api/mcp",
            "--header",
            "Authorization: Bearer <your-token>"
          ]
        }
      }
    }

    Note for LAN/HTTP users: If your JobSync URL uses http:// instead of https:// (common in home networks), you must add the --allow-http flag to the mcp-remote arguments.

    For other clients (OpenClaw, Hermes, etc.)

    Clients supporting streamable-http can connect directly:

    {
      "mcpServers": {
        "jobsync": {
          "type": "streamable-http",
          "url": "http://<your-jobsync-url>/api/mcp",
          "headers": { "Authorization": "Bearer <your-token>" }
        }
      }
    }
  5. Configure JobSync environment variables

    main

    You can customize your deployment by setting environment variables in your docker-compose.yml file.

    VariableDescription
    TZYour timezone (e.g. America/Edmonton). Set this on remote servers to avoid activity time shifts.
    AUTH_SECRETAuto-generated if not set. To set manually: openssl rand -base64 32
  6. Configure JobSync via Docker Compose

    main

    JobSync can be deployed using Docker Compose. The service runs on port 3737 and requires several environment variables for authentication, database connectivity, and AI provider integration.

    By default, the application uses a local SQLite database stored in the /data volume. To connect to an external database, provide a DATABASE_URL.

    Key Configuration Notes:

    • Persistence: Ensure the ./jobsyncdb/data directory is mapped to /data inside the container to persist the database.
    • AI Integration: You can enable or disable the MCP (Model Context Protocol) server using MCP_ENABLED. To use LLMs, provide the appropriate API keys for OpenAI, Gemini, DeepSeek, OpenRouter, or a local Ollama instance via OLLAMA_BASE_URL.
    services:
      app:
        image: ghcr.io/gsync/jobsync:latest
        container_name: jobsync_app
        ports:
          - "3737:3737"
        environment:
          - NODE_ENV=production
          - MCP_ENABLED=${MCP_ENABLED:-true}
          - DATABASE_URL=file:/data/dev.db
          - AUTH_SECRET=${AUTH_SECRET:-your-auth-secret}
          - ENCRYPTION_KEY=${ENCRYPTION_KEY:-you-encryption-key-here}
          - NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3737}
          - AUTH_TRUST_HOST=${AUTH_TRUST_HOST:-true}
          - TZ=${TZ:-America/Edmonton}
          - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
          - OPENAI_API_KEY=${OPENAI_API_KEY:-}
          - GEMINI_API_KEY=${GEMINI_API_KEY:-}
          - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-}
          - OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
          - RAPIDAPI_KEY=${RAPIDAPI_KEY:-}
        volumes:
          - ./jobsyncdb/data:/data
        extra_hosts:
          - "host.docker.internal:host-gateway"
        restart: unless-stopped
  7. Configure the Playwright Web Server

    main

    To run tests against a live instance of the application, the webServer configuration automatically starts the local development server.

    • command: The command used to start the server (e.g., npm run dev).
    • url: The URL to monitor to ensure the server is ready before tests start.
    • reuseExistingServer: If set to true, Playwright will use an existing server if it is already running on the specified URL.
    • timeout: The maximum time (in milliseconds) to wait for the server to become available.
    webServer: {
      command: "npm run dev",
      url: "http://localhost:3737",
      reuseExistingServer: true,
      timeout: 120 * 1000,
    }
  8. Configure JobSync with defineConfig

    main

    Use the defineConfig function from prisma/config to set up the JobSync Prisma configuration. This allows you to specify the location of your database schema and the directory where migrations are stored.

    import { defineConfig } from "prisma/config";
    
    export default defineConfig({
      schema: "./schema.prisma",
      migrations: {
        path: "./migrations",
      },
    });
  9. Configure Playwright E2E Test Settings

    main

    The project uses Playwright for end-to-end testing. Tests are located in the ./e2e directory. The configuration is optimized to prevent resource starvation on local machines by capping concurrency, which is necessary because the dev server and SQLite database can become slow under high load (e.g., during sequential server actions in resume flows).

    export default defineConfig({
      testDir: "./e2e",
      fullyParallel: true,
      forbidOnly: !!process.env.CI,
      retries: process.env.CI ? 2 : 0,
      workers: process.env.CI ? 1 : 4,
      timeout: 60_000,
      expect: { timeout: 10_000 },
      reporter: "html",
      use: {
        baseURL: "http://localhost:3737",
        trace: "on-first-retry",
      },
      projects: [
        { name: "chromium", use: { ...devices["Desktop Chrome"] } },
        { name: "firefox", use: { ...devices["Desktop Firefox"] } },
        { name: "webkit", use: { ...devices["Desktop Safari"] } },
      ],
      webServer: {
        command: "npm run dev",
        url: "http://localhost:3737",
        reuseExistingServer: true,
        timeout: 120 * 1000,
      },
    });
  10. Configure JobSync authentication with NextAuthConfig

    main

    JobSync uses next-auth for authentication. The authConfig object defines the authentication behavior, including custom pages, security secrets, and lifecycle callbacks.

    Custom Pages

    • pages.signIn: The route used for sign-in (set to /signin).
    • pages.error: The route used for error handling (set to /signin).

    Security

    • secret: Requires the AUTH_SECRET environment variable to be set for secure session management.

    Callbacks

    • authorized: Controls access to specific routes. In JobSync, users are required to be logged in to access /dashboard and /api routes. If a logged-in user attempts to access non-protected routes, they are redirected to /dashboard.
    • jwt: Extends the JWT token with the user's id from the user object.
    • session: Ensures the id from the JWT token is persisted into the session.user object, allowing the application to identify the user via session.user.id.
    export const authConfig = {
      pages: {
        signIn: "/signin",
        error: "/signin",
      },
      secret: process.env.AUTH_SECRET,
      callbacks: {
        authorized({ auth, request: { nextUrl } }) {
          const isLoggedIn = !!auth?.user;
          const isOnDashboard = nextUrl.pathname.startsWith("/dashboard");
          const isApiRoute = nextUrl.pathname.startsWith("/api");
    
          if (isOnDashboard || isApiRoute) {
            return isLoggedIn;
          } else if (isLoggedIn) {
            return Response.redirect(new URL("/dashboard", nextUrl));
          }
          return true;
        },
        async jwt({ token, user }) {
          if (user?.id) {
            token.id = user.id;
          }
          return token;
        },
        async session({ session, token }) {
          const userId = (token.id as string) || token.sub;
          if (userId) {
            session.user.id = userId;
          }
          return session;
        },
      },
      providers: [],
    } satisfies NextAuthConfig;
  11. Handle errors from the Job Match endpoint

    main

    When consuming the /api/ai/resume/match endpoint, handle the following specific error scenarios:

    Status CodeError ConditionAction
    401Not AuthenticatedEnsure the user session is valid and the user is logged in.
    400Missing Parameters or Preprocessing FailureCheck that resumeId, jobId, and selectedModel are provided. If preprocessing fails, check the error.message and error.code returned in the JSON body.
    429Rate Limit ExceededWait for the duration specified in the error message before retrying.
    503AI Service UnavailableCheck if the AI provider (e.g., Ollama) is running or if there is a network connection issue (ECONNREFUSED).
    500Internal Server ErrorGeneral failure; check server logs for details.
  12. Fetch user settings with getUserSettings

    main

    Retrieve the current authenticated user's settings. If no settings exist in the database, it returns the defaultUserSettings. The returned object contains a success boolean and a data object containing the userId and the merged settings object.

    const response = await getUserSettings();
    if (response.success) {
      console.log(response.data.settings);
    }