Skip to content

Conversation

@rgarcia
Copy link
Contributor

@rgarcia rgarcia commented Dec 22, 2025

Summary

Add hypeman cp command for copying files to/from running VM instances with docker cp compatible semantics.

Usage

# Copy file to instance
hypeman cp file.txt instance-id:/path/to/dest

# Copy from instance  
hypeman cp instance-id:/path/to/file ./local-dest

# Copy directory recursively
hypeman cp -r ./local-dir instance-id:/dest

# Stream tar from stdin
tar cf - ./files | hypeman cp - instance-id:/dest

# Stream tar to stdout
hypeman cp instance-id:/path - | tar xf -

# Archive mode (preserve UID/GID)
hypeman cp -a file.txt instance-id:/dest

Features

  • Copy files and directories to/from running instances
  • Docker cp compatible path semantics:
    • SRC_PATH/. copies contents, not the directory itself
    • Trailing slash handling for directory vs file detection
  • STDIN/STDOUT streaming with - argument for tar archives
  • Archive mode (-a) to preserve UID/GID ownership
  • Symlink handling with --follow-links flag
  • Quiet mode (-q) to suppress progress output

Dependencies

Requires corresponding changes in:


Note

Introduces a new cp subcommand enabling bi-directional file and directory copy between the local filesystem and instances with docker cp-compatible behavior.

  • Adds pkg/cmd/cp.go implementing copy to/from instance, including SRC/. and trailing-slash semantics, and robust path parsing (local vs instance:/path) with Windows path handling
  • Supports tar streaming via STDIN/STDOUT using -, including WebSocket cp endpoint usage and progress callbacks
  • Provides flags: --archive/-a (preserve UID/GID), --follow-links/-L, and --quiet/-q
  • Includes tar path sanitization to prevent path traversal and explicit UID/GID handling for created dirs/files
  • Registers the command in pkg/cmd/cmd.go; bumps github.com/onkernel/hypeman-go to v0.8.0 in go.mod/go.sum

Written by Cursor Bugbot for commit cdc3257. This will update automatically on new commits. Configure here.

@rgarcia rgarcia requested a review from sjmiller609 December 23, 2025 00:34
Add 'hypeman cp' command for copying files to/from running instances,
with docker cp compatible semantics:

Features:
- Copy files and directories to instances: hypeman cp file.txt instance:/path
- Copy from instances: hypeman cp instance:/path file.txt
- STDIN/STDOUT streaming with '-' argument for tar archives
- Recursive directory copying with proper tar packing/unpacking
- Symlink handling with --follow-links flag
- Archive mode (-a) to preserve UID/GID ownership
- Quiet mode (-q) to suppress progress output
- Docker cp path resolution semantics:
  - SRC_PATH/. copies contents, not the directory itself
  - Trailing slash handling for directory vs file detection

The command communicates with the API server via WebSocket,
streaming file data as binary chunks with JSON metadata headers.
Update to e893d90 with circular symlink detection and root ownership fix.
go.mod Outdated
google.golang.org/grpc v1.75.1 // indirect
)

replace github.com/onkernel/hypeman-go => ../hypeman-go
Copy link
Contributor Author

Choose a reason for hiding this comment

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

will update after hypeman-go merge

When copying a directory from instance to local, the /. suffix
(copy contents only) was computed but not used. Now:
- Without /.: creates source directory inside destination (docker cp semantics)
- With /.: copies contents directly into destination
if endMarker.Final {
receivedFinal = true
return nil
}
Copy link

Choose a reason for hiding this comment

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

Missing error handling for JSON unmarshal operations

The json.Unmarshal calls on lines 962 and 970 don't check for errors. If unmarshaling fails on line 962, endMarker.Final defaults to false, causing the function to continue looping even when the server sent a valid final end marker with an unexpected format. This would eventually produce a misleading "copy stream ended without completion marker" error instead of the actual parse error.

Additional Locations (1)

Fix in Cursor Fix in Web

case tar.TypeSymlink:
// TODO: Handle symlinks if needed
fmt.Fprintf(os.Stderr, "Warning: skipping symlink %s -> %s\n", header.Name, header.Linkname)
}
Copy link

Choose a reason for hiding this comment

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

Hard links in tar archives silently dropped without warning

The switch statement handling tar entry types only processes TypeDir, TypeReg, and TypeSymlink (which gets a warning). Hard links (tar.TypeLink) and other valid tar types like character devices, block devices, and FIFOs are silently ignored with no warning. This inconsistency is problematic because hard links are commonly used in tar archives for file deduplication. Users extracting tar archives containing hard links would experience silent data loss without any indication that files were skipped.

Fix in Cursor Fix in Web

Update to official v0.8.0 release which adds the file copy SDK functions
(CpToInstance, CpFromInstance).
@rgarcia rgarcia merged commit f67ad7b into main Dec 23, 2025
4 checks passed
@stainless-app stainless-app bot mentioned this pull request Dec 23, 2025
if !dstStat.Exists {
// DEST will be created
return dstPath, nil
}
Copy link

Choose a reason for hiding this comment

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

Missing trailing slash validation for directory copy destinations

When copying a directory to a non-existent destination path ending with a trailing slash (e.g., instance:/nonexistent/), the code proceeds instead of returning an error. For file sources, the code correctly checks dstEndsWithSlash && !dstExists and errors with "destination directory does not exist" (lines 341-343 and 636-638). However, the directory source branches at lines 362-365 and 652-653 skip this check. In docker cp, a trailing slash indicates "copy into this directory" and failing when that directory doesn't exist is expected behavior. This breaks the claimed docker cp path compatibility.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants