> For the complete documentation index, see [llms.txt](https://docs.elimity.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.elimity.com/sybase/step-by-step-deployment-guide.md).

# Step-by-step deployment guide

### 1. Configuring the gateway

The first step in setting up automatic imports via the Sybase gateway is configuring the gateway itself. Make sure your instance of Elimity Insights can reach the gateway, and that the gateway can reach your SAP ASE database server.

The gateway exposes its `HTTP` `API` on port `80`, which Elimity Insights uses to send import requests. Database connection details are read from `/app/config/config.json`. This file contains credentials, so mount it as a secret or read-only file and do not commit it to source control. The port value in this file is the SAP ASE database port, not the gateway HTTP port. SAP ASE commonly uses port 5000.

```
{
"connections": [     
    {       
        "server": "sybase.example.com",
        "port": 5000,
        "database": "my_database",
        "user": "my_user",
        "password": "my_password"
    }   
  ]
}
```

Edit the following properties in this file to configure the gateway to your needs:

{% hint style="info" %}
The `connections` property is a list. Each item in the list describes one SAP ASE database connection.
{% endhint %}

<table data-full-width="true"><thead><tr><th>Property</th><th width="202">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>connections</code></td><td><code>list[object]</code></td><td>JSON array describing the SAP ASE databases the gateway should connect to</td></tr><tr><td><code>connections[].server</code></td><td><code>string</code></td><td>Hostname or IP address of the SAP ASE server</td></tr><tr><td><code>connections[].port</code></td><td><code>option[number]</code></td><td>SAP ASE port, defaults to 5000</td></tr><tr><td><code>connections[].database</code></td><td><code>string</code></td><td>Database name</td></tr><tr><td><code>connections[].user</code></td><td><code>string</code></td><td>Database user</td></tr><tr><td><code>connections[].password</code></td><td><code>string</code></td><td>Database password</td></tr></tbody></table>

{% hint style="info" %}
The gateway only runs the `SELECT` queries you configure. Use a database user with read access only, and grant access only to the tables or views used by those queries.
{% endhint %}

The gateway uses `pymssql` over FreeTDS to connect to SAP ASE. No SAP Open Client SDK is required. There is no `tdsVersion` setting; FreeTDS negotiates the protocol version automatically.

**JWT validation**

We highly recommend requiring JWT validation to secure your gateway. Please read our official documentation about the following topics to understand how Elimity Insights authenticates to gateways via OAuth2:

* ​[Gateway-based imports](https://app.gitbook.com/o/rcHK1ouTJcSjbZfts4dH/sites/site_wUgaL/s/EDnPjX1DSBebgxMCuWjh/advanced-topics/gateway-based-imports)​
* ​[OAuth2 endpoint parameters for gateway authentication](https://app.gitbook.com/o/rcHK1ouTJcSjbZfts4dH/sites/site_wUgaL/s/EDnPjX1DSBebgxMCuWjh/server-configuration/oauth2-endpoint-parameters-for-gateway-authentication)

The Sybase gateway validates the JWT included in Elimity's import requests before running any SQL query. Requests without a JWT, or with an invalid or expired JWT, are rejected before any database query is executed.

Elimity Insights sends import requests to the gateway. The gateway validates the token using Elimity's authentication service, so the gateway must be able to reach that service when outbound network traffic is restricted.

For local manual testing only, you can set the environment variable `JWT_VALIDATION_OPTIONAL=true`. This allows requests with no `Authorization` header, for example when testing from a local dashboard without a real Elimity token. Invalid tokens are still rejected.

{% hint style="warning" %}
Do not enable `JWT_VALIDATION_OPTIONAL=true` in production.
{% endhint %}

### 2. Deploying the gateway <a href="#r2a" id="r2a"></a>

Since the gateway is distributed as a Docker image, our recommendation for deployment is to use a CaaS solution like Google Cloud Run or Azure Container Apps. If that is not an option, you can also manually deploy the image on a server that can reach your SAP ASE database. Refer to [our documentation about gateways and import agents](/technical-guides/gateways-and-import-agents.md) for additional details.

When deploying, configure:

* container port `80`
* a public or private gateway URL reachable by Elimity Insights
* a mounted `/app/config/config.json` file containing the SAP ASE connection details
* network access from the gateway to SAP ASE
* network access from the gateway to Elimity's authentication service for JWT validation

For example, a local Docker run command can look like this:

```
docker run \
  -p 8080:80 \
  -v /path/to/config.json:/app/config/config.json:ro \
  <sybase-gateway-image>
```

### &#x20;3. Creating a custom source in Elimity Insights

To set up automatic imports via the Sybase gateway, create a custom source in Elimity Insights first. If you are unsure about the data model, start with a single entity type. You can extend the data model later.

### 4. Enabling automatic imports

After creating the custom source, navigate to its detail page in Elimity Insights and open the `CONFIG` tab. Click `EDIT`, enter the gateway URL and the desired CRON schedule, and add the following configuration values:

<table data-full-width="true"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>entityTypes</code></td><td>JSON</td><td>JSON array describing how to import entities from SAP ASE</td></tr><tr><td><code>relationshipTypes</code></td><td>JSON</td><td>JSON array describing how to import relationships from SAP ASE</td></tr></tbody></table>

The following example imports users, roles and user-role relationships:

```
{
  "entityTypes": [
    {
      "id": "user",
      "query": "SELECT id, name, email, is_active FROM users",
      "attributes": [
        { "id": "email", "type": "string" },
        { "id": "is_active", "type": "boolean" }
      ]
    },
    {
      "id": "role",
      "query": "SELECT id, name FROM roles",
      "attributes": []
    }
  ],
  "relationshipTypes": [
    {
      "fromEntityType": "user",
      "toEntityType": "role",
      "query": "SELECT user_id, role_id FROM user_roles",
      "attributes": []
    }
  ]
}
```

#### Entity types

For each item in the `entityTypes` configuration value, the gateway performs an SQL query, converts the results into entities and sends those to Elimity Insights. More specifically, the `entityTypes` configuration value should be a JSON array of objects, where each object should have the following properties:

properties:

<table data-full-width="true"><thead><tr><th>Property</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>attributes</code></td><td><code>list[object]</code></td><td>Describes how to import attributes of the entity type's entities</td></tr><tr><td><code>attributes[].id</code></td><td><code>string</code></td><td>Unique identifier of the attribute type for which the gateway should import assignments</td></tr><tr><td><code>attributes[].type</code></td><td><code>string</code></td><td>Data type of the attribute type, one of <code>"boolean"</code>, <code>"date"</code>, <code>"dateTime"</code>, <code>"number"</code>, <code>"string"</code> or <code>"time"</code></td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>Unique identifier of the entity type for which the gateway should import entities</td></tr><tr><td><code>query</code></td><td><code>string</code></td><td>Query that the gateway should send to the configured SAP ASE databases</td></tr></tbody></table>

#### Relationship types <a href="#relationship-types" id="relationship-types"></a>

For each item in the `relationshipTypes` configuration value, the gateway performs an SQL query against each configured SAP ASE connection, converts the results into relationships and sends those to Elimity Insights. More specifically, the `relationshipTypes` configuration value should be a JSON array of objects, where each object should have the following properties:

| Property            | Type           | Description                                                                                                     |
| ------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- |
| `attributes`        | `list[object]` | Describes how to import attributes of the relationship type's relationships                                     |
| `attributes[].id`   | `string`       | Unique identifier of the attribute type for which the gateway should import assignments                         |
| `attributes[].type` | `string`       | Data type of the attribute type, one of `"boolean"`, `"date"`, `"dateTime"`, `"number"`, `"string"` or `"time"` |
| `fromEntityType`    | `string`       | Unique identifier of the entity type from which the relationships start                                         |
| `query`             | `string`       | Query that the gateway should send to the configured SAP ASE databases                                          |
| `toEntityType`      | `string`       | Unique identifier of the entity type where the relationships end                                                |

### Query format

When writing queries, make sure they follow these rules:

* for entity queries:
  * the number of output columns must equal the number of configured attributes plus two
  * the first output column represents the entity's id
  * the second output column represents the entity's name
  * the following output columns must type-match the configured attributes in the same order
* for relationship queries:
  * the number of output columns must equal the number of configured attributes plus two
  * the first output column represents the source entity's id
  * the second output column represents the target entity's id
  * the following output columns must type-match the configured attributes in the same order

ID and name columns, and relationship from/to id columns, are converted to strings by the gateway. `NULL`, empty strings and raw bytes are rejected. Numeric ids are accepted, so `SELECT id, name FROM users` works when `id` is numeric.

If you need explicit formatting, or if a column is returned by the driver as raw bytes, cast it in the query:

`SELECT convert(varchar, id), name FROM users`

The gateway does not generate attribute assignments for output columns that have a `NULL` value. Empty strings are skipped for string attributes. For non-string attributes, return either `NULL` or a valid value of the declared type.

### SAP ASE-specific data handling

#### ID and name columns

The gateway runs every configured query against each connection listed in `/app/config/config.json` and combines the results.&#x20;

{% hint style="warning" %}
If you configure more than one connection, make ids globally unique so entities from different databases do not merge
{% endhint %}

Entity ids, entity names and relationship ids are converted to strings. This keeps numeric primary keys easy to use:

`SELECT id, name FROM users`\
`SELECT user_id, role_id FROM user_roles`

The gateway rejects NULL, empty strings and raw bytes for these columns. If you import from multiple databases, build globally unique ids in SQL so entities from different databases do not merge accidentally:

`SELECT @@servername + '/' + db_name() + '/' + convert(varchar, id), name FROM users`

Use the same id format on both sides of relationship queries.

#### Empty VARCHAR values

SAP ASE does not store zero-length character strings as "". An inserted empty string is stored as a single space. The gateway does not trim or treat whitespace-only strings as empty; it passes the value through as returned by the database.

If a single space or whitespace-only value should mean "no assignment", convert it to NULL in your query:

`SELECT id, name, CASE WHEN ltrim(rtrim(email)) = '' THEN NULL ELSE email END FROM users`

#### DATE and TIME processing

With FreeTDS, SAP ASE `DATE` and `TIME` columns can be returned as raw bytes. The gateway does not decode those bytes. Cast date and time values explicitly in the query.

For a *date* attribute, cast `DATE` to `DATETIME`:

`SELECT id, name, convert(datetime, hire_date) FROM users`

For a *time* attribute, cast `TIME` to a string in `HH:MM:SS` format:

`SELECT id, name, convert(varchar, start_time, 108) FROM users`

`DATETIME, NUMERIC, BIT` and text columns can be returned as their normal database types.

#### Transaction isolation level

The gateway runs each connection's queries in a single transaction using SAP ASE isolation level 3 (`serializable`). This helps keep the imported data consistent, but large imports may keep read locks on the scanned tables until that connection's import finishes. During that time, writes to those tables may be blocked.&#x20;

{% hint style="warning" %}
Schedule large imports outside peak hours or narrow the SQL queries where possible.
{% endhint %}

**Troubleshooting**

| `HTTP` status `400` `Bad Request` before any SQL runs               | <p>The request is missing a JWT and JWT validation is required. </p><p>For local testing only, use <code>JWT\_VALIDATION\_OPTIONAL=true</code></p>              |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `HTTP status 401` before any SQL runs                               | The JWT is present but invalid or expired. Check that the gateway can reach the authentication service and that the token has not expired.                      |
| id/name column must not be null or id/name column must not be empty | The first two columns of an entity query, or the first two columns of a relationship query, returned an invalid id/name value. Fix the query to return a value. |
| id/name column returned raw bytes                                   | Cast the id/name column in SQL, for example `convert(varchar, col)`                                                                                             |
| column returned raw bytes ... cast it in the query                  | A `DATE, TIME` or binary attribute column was returned as raw bytes. Cast it in SQL.                                                                            |
| returned X columns but Y were expected                              | The query output does not match the configured attributes. Return exactly two leading columns plus one column per attribute.                                    |
| `users not found` or SAP ASE error `208`                            | The table does not exist in the configured database, or the query needs an owner qualified name such as dbo.users.                                              |
| Writes to scanned tables are blocked during import                  | The gateway uses isolation level 3 (serializable). **Schedule large imports outside peak hours or narrow the SQL queries.**                                     |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.elimity.com/sybase/step-by-step-deployment-guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
