Move troubleshooting and implementation docs to docs/
Relocate 30 non-essential .md files (investigation notes, fix summaries, implementation details, status reports) from the project root into docs/ to reduce clutter. Core operational docs (README, quickstart guides, configuration references) remain in the root. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
162
docs/KOSMI_IMAGE_UPLOAD.md
Normal file
162
docs/KOSMI_IMAGE_UPLOAD.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Kosmi Image Upload Protocol
|
||||
|
||||
## Summary
|
||||
|
||||
Kosmi uses a **simple HTTP POST** to upload images, **NOT the WebSocket**. Images are uploaded to a dedicated CDN endpoint.
|
||||
|
||||
## Upload Endpoint
|
||||
|
||||
```
|
||||
POST https://img.kosmi.io/
|
||||
```
|
||||
|
||||
## Request Details
|
||||
|
||||
### Method
|
||||
`POST`
|
||||
|
||||
### Headers
|
||||
```
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...
|
||||
Origin: https://app.kosmi.io
|
||||
Referer: https://app.kosmi.io/
|
||||
```
|
||||
|
||||
**Important:** No authentication required! The endpoint accepts anonymous uploads.
|
||||
|
||||
### Request Body
|
||||
Standard `multipart/form-data` with a single field:
|
||||
|
||||
```
|
||||
------WebKitFormBoundary...
|
||||
Content-Disposition: form-data; name="file"; filename="blurt.jpg"
|
||||
Content-Type: image/jpeg
|
||||
|
||||
[binary image data]
|
||||
------WebKitFormBoundary...--
|
||||
```
|
||||
|
||||
### CORS
|
||||
The endpoint has CORS enabled for `https://app.kosmi.io`:
|
||||
```
|
||||
Access-Control-Allow-Origin: https://app.kosmi.io
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
### Status
|
||||
`200 OK`
|
||||
|
||||
### Headers
|
||||
```
|
||||
Content-Type: application/json
|
||||
Access-Control-Allow-Origin: https://app.kosmi.io
|
||||
```
|
||||
|
||||
### Response Body (CONFIRMED)
|
||||
```json
|
||||
{
|
||||
"filename": "8d580b3a-905d-4bc9-909b-ccc6743edbdc.webp"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response contains only the filename, not the full URL. The full URL must be constructed as:
|
||||
```
|
||||
https://img.kosmi.io/{filename}
|
||||
```
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"filename": "3460a8e1-fe19-4371-a735-64078e9923a4.webp"
|
||||
}
|
||||
```
|
||||
→ Full URL: `https://img.kosmi.io/3460a8e1-fe19-4371-a735-64078e9923a4.webp`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### For Go Client
|
||||
|
||||
1. **No authentication needed** - This is a public upload endpoint
|
||||
2. **Use standard multipart/form-data** - Go's `mime/multipart` package
|
||||
3. **Set CORS headers**:
|
||||
- `Origin: https://app.kosmi.io`
|
||||
- `Referer: https://app.kosmi.io/`
|
||||
4. **Parse JSON response** to get the image URL
|
||||
5. **Send the URL to Kosmi chat** via the existing WebSocket `sendMessage` mutation
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Generate room code PNG image (already implemented in `roomcode_image.go`)
|
||||
2. Upload PNG to `https://img.kosmi.io/` via HTTP POST
|
||||
3. Parse response to get image URL
|
||||
4. Send message to Kosmi chat with the image URL
|
||||
5. Kosmi will automatically display the image as a thumbnail
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- The endpoint is public (no auth required)
|
||||
- Files are likely rate-limited or size-limited
|
||||
- Images are served from `img.kosmi.io` CDN
|
||||
- The upload is CORS-protected (only works from `app.kosmi.io` origin)
|
||||
|
||||
## Example Implementation (Pseudocode)
|
||||
|
||||
```go
|
||||
func UploadImageToKosmi(imageData []byte, filename string) (string, error) {
|
||||
// Create multipart form
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
// Add file field
|
||||
part, err := writer.CreateFormFile("file", filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
part.Write(imageData)
|
||||
writer.Close()
|
||||
|
||||
// Create request
|
||||
req, err := http.NewRequest("POST", "https://img.kosmi.io/", body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Set headers
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Origin", "https://app.kosmi.io")
|
||||
req.Header.Set("Referer", "https://app.kosmi.io/")
|
||||
|
||||
// Send request
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var result struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
|
||||
return result.URL, nil
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To test the upload, we can:
|
||||
1. Generate a test room code image
|
||||
2. Upload it to `https://img.kosmi.io/`
|
||||
3. Verify we get a URL back
|
||||
4. Send the URL to Kosmi chat
|
||||
5. Verify the image displays as a thumbnail
|
||||
|
||||
## References
|
||||
|
||||
- HAR capture: `image_upload_HAR-sanitized.har` (lines 3821-4110)
|
||||
- Upload request: Line 3910-4018
|
||||
- Upload response: Line 4019-4092
|
||||
|
||||
Reference in New Issue
Block a user