> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Runs reference

> File attachments and Run management in Sift

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

A Run is a defined time window during which telemetry is captured from one or more Assets. Runs provide the primary time context for organizing and analyzing telemetry.

## Concurrent Runs

Multiple Runs of the same Asset can be active simultaneously. Each data point is owned by the Run that recorded it, so concurrent Runs on the same Asset do not collide even when they write to the same channel name at the same timestamp.

This is the recommended pattern for parallel test sessions or CI workloads:

1. Create one Asset for the system under test (for example, `e1-sim`).
2. Start one Run per session, branch, or job. Name or tag each Run to make it identifiable later. For example, with a git SHA or CI job ID.

### Asset view behavior when no Run is selected

When viewing an Asset in Explore without selecting a specific Run, Sift surfaces the most recently received value across all Runs. To isolate the data from a particular session, select that Run explicitly from the Run picker.

## File attachments

Files can be attached to a Run as supplemental metadata. Attachments do not modify the Run or its telemetry.

### Supported file types

<MintTable
  columns={['Type', 'Formats', 'In-platform viewing']}
  rows={[
['Image', '`.png`, `.jpg`, `.jpeg`', 'Yes; viewable directly in Sift'],
['Video', '`.mp4`, `.webm`', 'Yes; viewable directly in Sift'],
['All other file types', 'Any format', 'No; files can be uploaded and downloaded but must be opened externally']
]}
/>

### Size limits

<MintTable
  columns={['Limit', 'Value']}
  rows={[
['Maximum file size per upload', '30 GB'],
['Maximum number of files per Run', 'No enforced limit']
]}
/>

### Common operations

<MintTable
  columns={['Operation', 'Where to access it in a Run', 'Notes']}
  rows={[
['Attach a file', '**Attach**', 'Supports all file types up to 30 GB'],
['Download an attached file', '**File attachments** section → hover over a file → **Download**', '—'],
['Download the original imported data file', '**File attachments** section → **Imported data file**', 'Only available for Runs created via file import']
]}
/>

## Related workflows

* [Import data from a file](/documentation/ingest/data-import/import-data-from-a-file)
* [Export data from a Run](/documentation/export/export-data-from-a-file)
