Bug Report: Remote Video Connections Write Malformed Entries to TAKServer Video Feed Manager
Summary
When a user creates a Remote Video Connection ("Publish Video Stream") in CloudTAK, the entry written to TAKServer's Video Feed Manager via the Marti API contains malformed field values — notably port -1 and null in fields where TAKServer expects populated data. These malformed entries cause two problems:
- iTAK fails to download the video feed list from TAKServer, throwing an "unknown error" when any malformed entry is present.
- TAKServer cannot edit the malformed entry via its own Video Feed Manager web UI (
/Marti/VideoUpload.jsp?feedId=N) — it returns "TAK Server resource unavailable or not allowed."
Deleting the CloudTAK-created entry from TAKServer resolves both issues immediately. Creating the same feed directly in TAKServer's Video Feed Manager produces a valid entry that works across all clients.
Environment
- CloudTAK Version: 12.103.0
- TAKServer Version: 5.6-RELEASE-22-HEAD
- CloudTAK Deploy: Docker Compose
- node-tak SDK Version: ^12.1.0
- Clients Tested: ATAK (Android), iTAK (iOS), CloudTAK (Web)
Steps to Reproduce
- In CloudTAK, navigate to Videos → Streams tab
- Click the Publish Video Stream button (camera+ icon)
- Create a Remote Video Connection with an HLS feed URL, e.g.:
- Alias:
TestHLSFeed
- Feed URL:
https://my-mediamtx-host.example.com/my-stream/index.m3u8
- Save the connection
- Observe the entry in TAKServer's Video Feed Manager (
/Marti/VideoManager.jsp)
Observed Behavior
Malformed Entry in TAKServer
The entry CloudTAK writes to TAKServer's Video Feed Manager contains invalid field values:
| Field |
Expected Value |
Actual Value (CloudTAK-created) |
| Protocol |
https |
(derived from URL but not sent) |
| Address |
my-mediamtx-host.example.com |
(derived from URL but not sent) |
| Port |
443 |
-1 |
| Path |
/my-stream/index.m3u8 |
(derived from URL but not sent) |
| Buffer time |
500 (or similar default) |
null |
| Network timeout |
5000 |
null |
| RTSP reliable |
0 or 1 |
null |
| Type |
VIDEO |
null |
When the same feed is created directly in TAKServer's Video Feed Manager, all fields are populated with valid defaults.
Impact on iTAK
With the malformed entry present in TAKServer's feed list:
- iTAK throws "unknown error" when attempting to download video feeds from the server
- All feeds become inaccessible, not just the malformed one — the entire feed list download fails
- Deleting the malformed entry immediately restores iTAK's ability to download feeds
Impact on TAKServer Admin
- Navigating to the edit page for the malformed entry (
/Marti/VideoUpload.jsp?feedId=N) returns: "TAK Server resource unavailable or not allowed."
- The entry can only be deleted from the list view, not edited
CloudTAK Behavior
- CloudTAK itself can play the feed it created (the HLS stream works)
- Other CloudTAK users can see and play the feed in their Streams tab
- The malformed fields do not affect CloudTAK playback
Expected Behavior
When CloudTAK creates a Remote Video Connection via POST /Marti/api/video, it should:
- Populate all required fields with valid values (especially
protocol, address, port, path, bufferTime, networkTimeout, type)
- For
https protocol feeds, set port to 443; for rtsp, set port to 8554; etc.
- Ensure the entry is editable in TAKServer's Video Feed Manager web UI
- Ensure the entry does not break feed list serialization for other TAK clients (iTAK, ATAK)
Root Cause
The bug spans the node-tak SDK and the CloudTAK frontend. There is a fundamental design mismatch between how CloudTAK creates video feeds and what TAKServer's Marti API expects.
The Mismatch
TAKServer's Video Feed Manager stores feeds as decomposed fields: protocol, address, port, path, networkTimeout, bufferTime, rtspReliable, type, etc. CloudTAK's frontend and the node-tak SDK send feeds with a single url field and omit all the decomposed fields. TAKServer accepts the request but stores -1/null for every missing field, producing a record that breaks other clients.
Trace Through Each Layer
Layer 1 — Frontend form (VideosRemoteFeed.vue)
The feed form collects only 3 user-editable fields:
alias → text input ("Feed Name")
url → text input ("Feed URL")
active → toggle
No fields exist for protocol, port, address, path, bufferTime, networkTimeout, rtspReliable, or type.
Layer 2 — Frontend save logic (MenuVideosRemote.vue)
The newFeed() function (line 136) creates a feed with only { uuid, alias, url, active }. The saveConnection() function (line 160) posts this directly as the request body with no field enrichment or URL decomposition.
Layer 3 — SDK (@tak-ps/node-tak, video.js)
The FeedInput schema defines only 4 fields:
export const FeedInput = Type.Object({
uuid: Type.Optional(Type.String()),
active: Type.Boolean({ default: true }),
alias: Type.String(),
url: Type.String(),
});
The create() method wraps the connection and posts it to TAKServer:
await this.api.fetch(url, {
method: 'POST',
body: {
videoConnections: [{
uuid,
...connection,
feeds: connection.feeds.map((feed) => {
return {
uuid: randomUUID(),
...feed, // ← only uuid, alias, url, active
};
})
}]
}
});
No URL parsing, no default injection, no decomposition into TAKServer's expected fields.
Layer 4 — TAKServer (POST /Marti/api/video)
TAKServer receives a feed with only uuid, alias, url, and active. It accepts the request without validation but stores null/default values for all missing structured fields. The resulting record has port: -1, bufferTime: null, type: null, etc.
What CloudTAK Sends
{
"videoConnections": [{
"uuid": "...",
"active": true,
"alias": "TestHLSFeed",
"feeds": [{
"uuid": "...",
"active": true,
"alias": "TestHLSFeed",
"url": "https://my-mediamtx-host.example.com/my-stream/index.m3u8"
}]
}]
}
What TAKServer Expects (Based on Working Manual Entry)
{
"videoConnections": [{
"uuid": "...",
"active": true,
"alias": "TestHLSFeed",
"feeds": [{
"uuid": "...",
"active": true,
"alias": "TestHLSFeed",
"protocol": "https",
"address": "my-mediamtx-host.example.com",
"port": 443,
"path": "/my-stream/index.m3u8",
"networkTimeout": 5000,
"bufferTime": 500,
"rtspReliable": "0",
"type": "VIDEO"
}]
}]
}
Suggested Fix
The fix could be applied at the SDK level, the frontend level, or both:
Option A — SDK-level URL decomposition (@tak-ps/node-tak)
In FeedInput, accept the url field but decompose it in create() before posting to TAKServer:
// Pseudocode for create() in video.js
const parsed = new URL(feed.url);
return {
uuid: randomUUID(),
active: feed.active,
alias: feed.alias,
protocol: parsed.protocol.replace(':', ''), // "https"
address: parsed.hostname, // "host.example.com"
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search, // "/stream/index.m3u8"
networkTimeout: feed.networkTimeout || 5000,
bufferTime: feed.bufferTime || 500,
rtspReliable: feed.rtspReliable || "0",
type: "VIDEO",
...feed, // allow explicit overrides
};
Option B — Frontend field expansion (CloudTAK)
Add optional fields to VideosRemoteFeed.vue for port, bufferTime, networkTimeout, etc., with sensible defaults. This gives advanced users control while keeping the simple URL workflow.
Ideally both — the SDK should handle URL decomposition as a safety net, and the UI should expose the additional fields for users who need them.
Workaround
Create HLS (or any protocol) video feeds directly in TAKServer's Video Feed Manager (/Marti/VideoUpload.jsp) instead of through CloudTAK's Remote Video Connection interface. Example working configuration:
| Field |
Value |
| Active |
✓ (checked) |
| Alias |
TestHLSFeed |
| Protocol |
https |
| Address |
my-mediamtx-host.example.com |
| Port |
443 |
| Path |
/my-stream/index.m3u8 |
| Network timeout (ms) |
5000 |
| Buffer time (ms) |
500 |
Feeds created this way are visible and playable across all clients: CloudTAK, iTAK, and ATAK.
Affected Repos
dfpc-coe/node-tak — FeedInput schema is missing TAKServer's required fields; create() does not decompose URL or inject defaults
dfpc-coe/CloudTAK — VideosRemoteFeed.vue only collects alias, url, and active; MenuVideosRemote.vue posts incomplete feed objects
Related Files
| File |
Repo |
Role |
dist/lib/api/video.js |
@tak-ps/node-tak |
SDK — FeedInput type, create() method |
api/routes/marti-video.ts (lines 61–78) |
dfpc-coe/CloudTAK |
Backend route proxying POST /api/marti/video to TAKServer |
api/web/src/components/CloudTAK/Menu/MenuVideosRemote.vue |
dfpc-coe/CloudTAK |
Frontend — connection save logic |
api/web/src/components/CloudTAK/Menu/Videos/VideosRemoteFeed.vue |
dfpc-coe/CloudTAK |
Frontend — feed form (alias, url, active only) |
Bug Report: Remote Video Connections Write Malformed Entries to TAKServer Video Feed Manager
Summary
When a user creates a Remote Video Connection ("Publish Video Stream") in CloudTAK, the entry written to TAKServer's Video Feed Manager via the Marti API contains malformed field values — notably port
-1andnullin fields where TAKServer expects populated data. These malformed entries cause two problems:/Marti/VideoUpload.jsp?feedId=N) — it returns "TAK Server resource unavailable or not allowed."Deleting the CloudTAK-created entry from TAKServer resolves both issues immediately. Creating the same feed directly in TAKServer's Video Feed Manager produces a valid entry that works across all clients.
Environment
Steps to Reproduce
TestHLSFeedhttps://my-mediamtx-host.example.com/my-stream/index.m3u8/Marti/VideoManager.jsp)Observed Behavior
Malformed Entry in TAKServer
The entry CloudTAK writes to TAKServer's Video Feed Manager contains invalid field values:
httpsmy-mediamtx-host.example.com443-1/my-stream/index.m3u8500(or similar default)null5000null0or1nullVIDEOnullWhen the same feed is created directly in TAKServer's Video Feed Manager, all fields are populated with valid defaults.
Impact on iTAK
With the malformed entry present in TAKServer's feed list:
Impact on TAKServer Admin
/Marti/VideoUpload.jsp?feedId=N) returns: "TAK Server resource unavailable or not allowed."CloudTAK Behavior
Expected Behavior
When CloudTAK creates a Remote Video Connection via
POST /Marti/api/video, it should:protocol,address,port,path,bufferTime,networkTimeout,type)httpsprotocol feeds, set port to443; forrtsp, set port to8554; etc.Root Cause
The bug spans the
node-takSDK and the CloudTAK frontend. There is a fundamental design mismatch between how CloudTAK creates video feeds and what TAKServer's Marti API expects.The Mismatch
TAKServer's Video Feed Manager stores feeds as decomposed fields:
protocol,address,port,path,networkTimeout,bufferTime,rtspReliable,type, etc. CloudTAK's frontend and thenode-takSDK send feeds with a singleurlfield and omit all the decomposed fields. TAKServer accepts the request but stores-1/nullfor every missing field, producing a record that breaks other clients.Trace Through Each Layer
Layer 1 — Frontend form (
VideosRemoteFeed.vue)The feed form collects only 3 user-editable fields:
No fields exist for
protocol,port,address,path,bufferTime,networkTimeout,rtspReliable, ortype.Layer 2 — Frontend save logic (
MenuVideosRemote.vue)The
newFeed()function (line 136) creates a feed with only{ uuid, alias, url, active }. ThesaveConnection()function (line 160) posts this directly as the request body with no field enrichment or URL decomposition.Layer 3 — SDK (
@tak-ps/node-tak,video.js)The
FeedInputschema defines only 4 fields:The
create()method wraps the connection and posts it to TAKServer:No URL parsing, no default injection, no decomposition into TAKServer's expected fields.
Layer 4 — TAKServer (
POST /Marti/api/video)TAKServer receives a feed with only
uuid,alias,url, andactive. It accepts the request without validation but stores null/default values for all missing structured fields. The resulting record hasport: -1,bufferTime: null,type: null, etc.What CloudTAK Sends
{ "videoConnections": [{ "uuid": "...", "active": true, "alias": "TestHLSFeed", "feeds": [{ "uuid": "...", "active": true, "alias": "TestHLSFeed", "url": "https://my-mediamtx-host.example.com/my-stream/index.m3u8" }] }] }What TAKServer Expects (Based on Working Manual Entry)
{ "videoConnections": [{ "uuid": "...", "active": true, "alias": "TestHLSFeed", "feeds": [{ "uuid": "...", "active": true, "alias": "TestHLSFeed", "protocol": "https", "address": "my-mediamtx-host.example.com", "port": 443, "path": "/my-stream/index.m3u8", "networkTimeout": 5000, "bufferTime": 500, "rtspReliable": "0", "type": "VIDEO" }] }] }Suggested Fix
The fix could be applied at the SDK level, the frontend level, or both:
Option A — SDK-level URL decomposition (
@tak-ps/node-tak)In
FeedInput, accept theurlfield but decompose it increate()before posting to TAKServer:Option B — Frontend field expansion (
CloudTAK)Add optional fields to
VideosRemoteFeed.vueforport,bufferTime,networkTimeout, etc., with sensible defaults. This gives advanced users control while keeping the simple URL workflow.Ideally both — the SDK should handle URL decomposition as a safety net, and the UI should expose the additional fields for users who need them.
Workaround
Create HLS (or any protocol) video feeds directly in TAKServer's Video Feed Manager (
/Marti/VideoUpload.jsp) instead of through CloudTAK's Remote Video Connection interface. Example working configuration:TestHLSFeedhttpsmy-mediamtx-host.example.com443/my-stream/index.m3u85000500Feeds created this way are visible and playable across all clients: CloudTAK, iTAK, and ATAK.
Affected Repos
dfpc-coe/node-tak—FeedInputschema is missing TAKServer's required fields;create()does not decompose URL or inject defaultsdfpc-coe/CloudTAK—VideosRemoteFeed.vueonly collectsalias,url, andactive;MenuVideosRemote.vueposts incomplete feed objectsRelated Files
dist/lib/api/video.js@tak-ps/node-takFeedInputtype,create()methodapi/routes/marti-video.ts(lines 61–78)dfpc-coe/CloudTAKPOST /api/marti/videoto TAKServerapi/web/src/components/CloudTAK/Menu/MenuVideosRemote.vuedfpc-coe/CloudTAKapi/web/src/components/CloudTAK/Menu/Videos/VideosRemoteFeed.vuedfpc-coe/CloudTAK