# Source files
URL: https://synquic.tech/docs/api/sources.md

Stage generated files or a directory, inspect the source, and deploy it.

Source sessions hold uploaded application files until deployment. The SDK accepts a directory or an
in-memory file map. REST accepts a compressed archive through a short-lived upload session.

## Deploy generated files

The SDK's `deploy()` helper combines staging, detection, project selection, and deployment submission.
Use it when your application or AI workflow produces files:

```ts
const submitted = await ship.deploy({
  name: "generated-site",
  source: {
    type: "files",
    files: { "index.html": "<h1>Hello from Synq</h1>" },
  },
});

const outcome = await ship.deployment(submitted.deployment_id).wait({ timeoutMs: 120_000 });
if (!outcome.success) throw new Error(outcome.message ?? outcome.status);
```

File keys are relative paths; values may be strings or `Uint8Array` data. Use
`{ type: "directory", path: "/absolute/path/to/site" }` for a directory. With a remote client, files are
packed on the calling machine. A native directory must be inside an allowed `policy.sourceRoots` entry.

| `deploy()` option | Use |
| --- | --- |
| `source` | Required file map or absolute directory path. |
| `name` | Suggested project name. |
| `projectId` | Deploy into an existing authorized project. |
| `environment` | `production` or `preview`. |
| `serverId` | Authorized target server; omit to use the configured target. |
| `serviceIds` | Selected detected services. |
| `onStep` | Source-workflow progress callback. |
| `signal` | Abort supported source work; a submitted deployment requires explicit cancellation. |

The result contains `deployment_id`, `project_id`, and optional `configDiagnostics`. Submission and
readiness are separate. See [deployment outcomes](/docs/api/deployments#wait-decisions-and-cancellation).

## Upload a source folder

For a custom uploader, open a session first:

<Tabs items={['SDK', 'REST API']} groupId="api-transport" persist>
<Tab value="SDK">

```ts
const session = await ship.sources.open({ name: "my-site" });
console.log(session.sessionId, session.upload);
```

</Tab>
<Tab value="REST API">

```bash
curl -X POST "$SYNQ_URL/api/projects/folder/session" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-site"}'
```

</Tab>
</Tabs>

The response includes `sessionId`, `expiresAt`, and `upload` instructions:

| Upload field | Use |
| --- | --- |
| `absoluteUrl`, `method` | Destination and HTTP method for the archive. |
| `headers` | Required upload headers. |
| `requiresAuth` | Send your bearer credential only when required. |
| `withCredentials` | Whether a browser upload needs credentials. |

Send a gzip-compressed tar archive to the returned URL. Use the supplied instructions because the upload
destination can differ by deployment mode. The self-hosted relay is
`POST /api/projects/folder/upload/:sessionId` and limits uploads to 300 MB.

The SDK can perform both session creation and upload for you:

```ts
const staged = await ship.sources.stage({
  name: "my-site",
  source: { type: "directory", path: "/absolute/path/to/site" },
});
console.log(staged.sessionId, staged.expiresAt);
```

Directory packing excludes `.git`, `node_modules`, and `.DS_Store`. It enforces path, entry, and size
limits and rejects escaping symlinks and special files. A system `tar` executable is not required by the SDK.

## Inspect an uploaded source

<Tabs items={['SDK', 'REST API']} groupId="api-transport" persist>
<Tab value="SDK">

```ts
const scan = await ship.sources.scan("session_123", { includeEnv: true });
console.log(scan.stack, scan.buildCommand, scan.configDiagnostics);
```

</Tab>
<Tab value="REST API">

```bash
curl -X POST "$SYNQ_URL/api/projects/folder/scan/session_123" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"includeEnv":true}'
```

</Tab>
</Tabs>

Detection returns the stack, package manager, commands, output paths, service definitions, and any
configuration diagnostics. Inspect errors before submitting a deployment. Environment values are
masked by default. The example requests editable values in the scan response with `includeEnv: true`;
see [Reveal environment values](/docs/api/services#reveal-environment-values).

To finish a custom flow, use `projects.ensure()` to record the chosen configuration, then
`deployments.buildAccess()` with `projectId` and `uploadSessionId`. The shared fields are documented in
[projects](/docs/api/projects) and [deployments](/docs/api/deployments#deploy-wizard-buildaccess).

## Upload an app archive

On a self-hosted instance, open the session with `"archive": true` and upload a `.zip`, `.tar`,
`.tar.gz`, `.tar.bz2`, `.tar.xz` or `.tar.zst` file as-is instead of a packed folder. The format is
detected from the file's bytes, a single wrapping top-level folder is unwrapped, and extraction rejects
paths or symbolic links that would leave the archive. The same 300 MB limit applies to the upload and to
the unpacked source, whatever the compression. 7z and rar archives are refused. Scan and deploy the
session as above.

## Import from a Git URL

A self-hosted instance can deploy any HTTPS or SSH repository (GitLab, Bitbucket, Gitea, Forgejo or
your own server). Open an import with the access method, test access, then fetch the branch to scan:

```bash
curl -X POST "$SYNQ_URL/api/projects/git/session" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://gitlab.com/acme/web.git","authMode":"token","token":"'"$GITLAB_TOKEN"'"}'

curl -X POST "$SYNQ_URL/api/projects/git/session/$SESSION_ID/check" \
  -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" -d '{}'

curl -X POST "$SYNQ_URL/api/projects/git/session/$SESSION_ID/fetch" \
  -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" \
  -d '{"branch":"main"}'
```

With `"authMode":"ssh"` the response carries a generated `publicKey`: add it to the repository as a
read-only deploy key before the check. The token is stored encrypted and never returned. Scan the same
session id, then call `projects.ensure()` with `gitSessionId` set to it and deploy with
`deployments.buildAccess()` without `uploadSessionId`, so every build clones the remote. The project's
push webhook (`GET /api/projects/:id/git-source/webhook`) deploys pushes to its branch.

## Operations

{/* api-operations:start */}

### Resource methods

| Operation | SDK | REST API |
| --- | --- | --- |
| Package and stage generated files or an allowed directory. | `sources.stage(input, options?)` | [Upload workflow](/docs/api/sources#upload-a-source-folder) |
| Open a folder upload session and return its upload instructions. | `sources.open(input?)` | `POST /api/projects/folder/session`<br />`project:write` |
| Detect build configuration from a staged source session. | `sources.scan(id, options?)` | `POST /api/projects/folder/scan/:sessionId`<br />`project:write` |
| Reveal explicitly requested source values after authorization. | `sources.reveal(id, input)` | `POST /api/projects/folder/scan/:sessionId/env-reveal`<br />`project:write` |
| Start a Git URL import (any https or SSH remote) with no credential, an access token, or a generated deploy key. | `sources.openGit(input)` | `POST /api/projects/git/session`<br />`project:write` · Self-hosted |
| Test access to the import's remote and list its branches; pins SSH host keys on first connect. | `sources.checkGit(id, input?)` | `POST /api/projects/git/session/:sessionId/check`<br />`project:write` · Self-hosted |
| Shallow-clone one branch into the import session so the source scan can detect the build. | `sources.fetchGit(id, input)` | `POST /api/projects/git/session/:sessionId/fetch`<br />`project:write` · Self-hosted |

### HTTP endpoints

| Operation | SDK | REST API |
| --- | --- | --- |
| Upload the gzipped tarball, or a .zip, .tar, .tar.gz, .tar.bz2, .tar.xz or .tar.zst app archive for an archive session (self-hosted relay, 300 MB limit). | HTTP only | `POST /api/projects/folder/upload/:sessionId`<br />`project:write` · Self-hosted |

{/* api-operations:end */}
