Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ struct InlineVideoAttachmentView: View {

/// Fetch attachment base64 data from the daemon HTTP endpoint.
private func fetchAttachmentData(port: Int, attachmentId: String) async throws -> String {
guard let token = await SessionTokenManager.getTokenAsync() else {
guard let token = readHttpToken() else {
throw URLError(.userAuthenticationRequired)
}
let url = URL(string: "http://localhost:\(port)/v1/attachments/\(attachmentId)")!
Expand Down
12 changes: 12 additions & 0 deletions clients/shared/IPC/DaemonClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ func readSessionToken(environment: [String: String]? = nil) -> String? {
}
return token
}

/// Read the daemon HTTP bearer token from disk (~/.vellum/http-token).
func readHttpToken() -> String? {
let tokenPath = NSHomeDirectory() + "/.vellum/http-token"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve HTTP token path via BASE_DATA_DIR

Use the same root-dir resolution as the daemon when reading http-token; this helper currently hardcodes ~/.vellum/http-token, but the daemon writes the bearer token under (BASE_DATA_DIR || $HOME)/.vellum/http-token (assistant/src/util/platform.ts + assistant/src/daemon/lifecycle.ts). In environments that set BASE_DATA_DIR (custom data roots, containerized/dev setups), readHttpToken() returns nil, so lazy-loaded video requests are sent without a valid token and fail with 401.

Useful? React with 👍 / 👎.

guard let data = try? Data(contentsOf: URL(fileURLWithPath: tokenPath)),
let token = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty else {
return nil
}
return token
}
Comment on lines +56 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 readHttpToken() ignores BASE_DATA_DIR, unlike readSessionToken() and other path resolvers

The new readHttpToken() hardcodes the path as NSHomeDirectory() + "/.vellum/http-token", but does not respect the BASE_DATA_DIR environment variable. This is inconsistent with the existing readSessionToken() function at clients/shared/IPC/DaemonClient.swift:35-41 which explicitly checks BASE_DATA_DIR and the daemon-side getHttpTokenPath() at assistant/src/util/platform.ts:146-148 which uses getRootDir() (also BASE_DATA_DIR-aware).

Root Cause and Impact

On the daemon side, getHttpTokenPath() returns join(getRootDir(), 'http-token') where getRootDir() resolves to join(process.env.BASE_DATA_DIR || homedir(), '.vellum'). When BASE_DATA_DIR is set (e.g. in Docker via ENV BASE_DATA_DIR=/data, or in development), the daemon writes the http-token to $BASE_DATA_DIR/.vellum/http-token.

The Swift readHttpToken() always reads from ~/.vellum/http-token, so when BASE_DATA_DIR is set to a non-home directory, the token file won't be found and the function returns nil, causing fetchAttachmentData() to throw URLError(.userAuthenticationRequired) — the same class of auth failure this PR was meant to fix.

The existing readSessionToken() in the same file correctly handles this:

func resolveSessionTokenPath(environment: [String: String]? = nil) -> String {
    let env = environment ?? ProcessInfo.processInfo.environment
    if let baseDir = env["BASE_DATA_DIR"]?..., !baseDir.isEmpty {
        return expandHomePath(baseDir) + "/.vellum/session-token"
    }
    return NSHomeDirectory() + "/.vellum/session-token"
}

Impact: Video lazy-load will fail with 401 when BASE_DATA_DIR is set to a non-default path.

Suggested change
func readHttpToken() -> String? {
let tokenPath = NSHomeDirectory() + "/.vellum/http-token"
guard let data = try? Data(contentsOf: URL(fileURLWithPath: tokenPath)),
let token = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty else {
return nil
}
return token
}
/// Resolve the daemon HTTP token path.
/// Uses BASE_DATA_DIR when set to match daemon root resolution.
func resolveHttpTokenPath(environment: [String: String]? = nil) -> String {
let env = environment ?? ProcessInfo.processInfo.environment
if let baseDir = env["BASE_DATA_DIR"]?.trimmingCharacters(in: .whitespacesAndNewlines), !baseDir.isEmpty {
return expandHomePath(baseDir) + "/.vellum/http-token"
}
return NSHomeDirectory() + "/.vellum/http-token"
}
/// Read the daemon HTTP bearer token from disk (~/.vellum/http-token).
func readHttpToken(environment: [String: String]? = nil) -> String? {
let tokenPath = resolveHttpTokenPath(environment: environment)
guard let data = try? Data(contentsOf: URL(fileURLWithPath: tokenPath)),
let token = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty else {
return nil
}
return token
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

#endif

/// Protocol for daemon client communication, enabling dependency injection and testing.
Expand Down
Loading