219 lines
4.8 KiB
Markdown
219 lines
4.8 KiB
Markdown
|
|
# Kosmi API Reverse Engineering Findings
|
||
|
|
|
||
|
|
## Key Discovery: HTTP POST Works!
|
||
|
|
|
||
|
|
After browser-based investigation, we discovered that Kosmi's GraphQL API works via **HTTP POST**, not just WebSocket!
|
||
|
|
|
||
|
|
### Working Endpoint
|
||
|
|
|
||
|
|
```
|
||
|
|
POST https://engine.kosmi.io/
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"query": "{ __schema { types { name } } }"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response**: ✅ 200 OK with proper GraphQL response
|
||
|
|
|
||
|
|
### WebSocket Issue
|
||
|
|
|
||
|
|
The WebSocket endpoint `wss://engine.kosmi.io/gql-ws` returns **403 Forbidden** when connecting directly because:
|
||
|
|
|
||
|
|
1. **Missing Cookies**: The browser has session cookies that aren't accessible via `document.cookie` (HTTP-only)
|
||
|
|
2. **Missing Headers**: Likely needs `Origin: https://app.kosmi.io` header
|
||
|
|
3. **Session Required**: May need to establish a session first via HTTP
|
||
|
|
|
||
|
|
## Implementation Options
|
||
|
|
|
||
|
|
### Option 1: HTTP POST with Polling (Recommended for Now)
|
||
|
|
|
||
|
|
**Pros**:
|
||
|
|
- ✅ Works without authentication
|
||
|
|
- ✅ Simple implementation
|
||
|
|
- ✅ No WebSocket complexity
|
||
|
|
|
||
|
|
**Cons**:
|
||
|
|
- ❌ Not real-time (need to poll)
|
||
|
|
- ❌ Higher latency
|
||
|
|
- ❌ More bandwidth usage
|
||
|
|
|
||
|
|
**Implementation**:
|
||
|
|
```go
|
||
|
|
// Poll for new messages every 1-2 seconds
|
||
|
|
func (c *GraphQLClient) PollMessages() {
|
||
|
|
ticker := time.NewTicker(2 * time.Second)
|
||
|
|
for range ticker.C {
|
||
|
|
// Query for messages since last timestamp
|
||
|
|
messages := c.QueryMessages(lastTimestamp)
|
||
|
|
// Process new messages
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 2: WebSocket with Session Cookies
|
||
|
|
|
||
|
|
**Pros**:
|
||
|
|
- ✅ Real-time updates
|
||
|
|
- ✅ Efficient (push-based)
|
||
|
|
- ✅ Lower latency
|
||
|
|
|
||
|
|
**Cons**:
|
||
|
|
- ❌ Requires session establishment
|
||
|
|
- ❌ Need to handle cookies
|
||
|
|
- ❌ More complex
|
||
|
|
|
||
|
|
**Implementation**:
|
||
|
|
```go
|
||
|
|
// 1. First, establish session via HTTP
|
||
|
|
session := establishSession()
|
||
|
|
|
||
|
|
// 2. Then connect WebSocket with cookies
|
||
|
|
dialer := websocket.Dialer{
|
||
|
|
Jar: session.CookieJar,
|
||
|
|
}
|
||
|
|
conn, _, err := dialer.Dial(wsURL, http.Header{
|
||
|
|
"Origin": []string{"https://app.kosmi.io"},
|
||
|
|
"Cookie": []string{session.Cookies},
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 3: Hybrid Approach
|
||
|
|
|
||
|
|
**Best of both worlds**:
|
||
|
|
1. Use HTTP POST for sending messages (mutations)
|
||
|
|
2. Use HTTP POST polling for receiving messages (queries)
|
||
|
|
3. Later upgrade to WebSocket when we figure out auth
|
||
|
|
|
||
|
|
## Next Steps
|
||
|
|
|
||
|
|
### Immediate (HTTP-based)
|
||
|
|
|
||
|
|
1. ✅ Update `graphql.go` to use HTTP POST instead of WebSocket
|
||
|
|
2. ✅ Implement message polling
|
||
|
|
3. ✅ Test with actual Kosmi room
|
||
|
|
4. ✅ Verify message sending works
|
||
|
|
|
||
|
|
### Future (WebSocket-based)
|
||
|
|
|
||
|
|
1. ⏳ Figure out session establishment
|
||
|
|
2. ⏳ Extract cookies from browser or create session
|
||
|
|
3. ⏳ Update WebSocket connection to include cookies
|
||
|
|
4. ⏳ Switch from polling to real-time subscriptions
|
||
|
|
|
||
|
|
## GraphQL Schema Discovery
|
||
|
|
|
||
|
|
From the introspection query, we found these types:
|
||
|
|
- `RootQueryType` - For queries
|
||
|
|
- `RootMutationType` - For mutations
|
||
|
|
- `Session` - Session management
|
||
|
|
- `Success` - Success responses
|
||
|
|
|
||
|
|
We need to explore the schema more to find:
|
||
|
|
- Message query fields
|
||
|
|
- Message mutation fields
|
||
|
|
- Room/channel structures
|
||
|
|
|
||
|
|
## Testing Commands
|
||
|
|
|
||
|
|
### Test HTTP Endpoint
|
||
|
|
```bash
|
||
|
|
curl -X POST https://engine.kosmi.io/ \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"query": "{ __schema { types { name } } }"}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test with Go
|
||
|
|
```go
|
||
|
|
resp, err := http.Post(
|
||
|
|
"https://engine.kosmi.io/",
|
||
|
|
"application/json",
|
||
|
|
strings.NewReader(`{"query": "{ __schema { types { name } } }"}`),
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Browser Findings
|
||
|
|
|
||
|
|
### Cookies Present
|
||
|
|
```
|
||
|
|
g_state={...}
|
||
|
|
```
|
||
|
|
Plus HTTP-only cookies we can't access.
|
||
|
|
|
||
|
|
### User Agent
|
||
|
|
```
|
||
|
|
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
|
||
|
|
```
|
||
|
|
|
||
|
|
### Origin
|
||
|
|
```
|
||
|
|
https://app.kosmi.io
|
||
|
|
```
|
||
|
|
|
||
|
|
## Recommendations
|
||
|
|
|
||
|
|
**For MVP**: Use HTTP POST with polling
|
||
|
|
- Simpler to implement
|
||
|
|
- Works without authentication
|
||
|
|
- Good enough for initial testing
|
||
|
|
- Can upgrade to WebSocket later
|
||
|
|
|
||
|
|
**For Production**: Figure out WebSocket auth
|
||
|
|
- Better performance
|
||
|
|
- Real-time updates
|
||
|
|
- Lower bandwidth
|
||
|
|
- Better user experience
|
||
|
|
|
||
|
|
## Updated Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
IRC Message
|
||
|
|
↓
|
||
|
|
Matterbridge
|
||
|
|
↓
|
||
|
|
Kosmi Bridge (HTTP POST)
|
||
|
|
↓
|
||
|
|
POST https://engine.kosmi.io/
|
||
|
|
↓
|
||
|
|
Kosmi Room
|
||
|
|
|
||
|
|
Kosmi Room
|
||
|
|
↓
|
||
|
|
Poll https://engine.kosmi.io/ (every 2s)
|
||
|
|
↓
|
||
|
|
Kosmi Bridge
|
||
|
|
↓
|
||
|
|
Matterbridge
|
||
|
|
↓
|
||
|
|
IRC Message
|
||
|
|
```
|
||
|
|
|
||
|
|
## Action Items
|
||
|
|
|
||
|
|
1. **Update `graphql.go`**:
|
||
|
|
- Replace WebSocket with HTTP client
|
||
|
|
- Implement POST request method
|
||
|
|
- Add polling loop for messages
|
||
|
|
|
||
|
|
2. **Test queries**:
|
||
|
|
- Find the correct query for fetching messages
|
||
|
|
- Find the correct mutation for sending messages
|
||
|
|
- Test with actual room ID
|
||
|
|
|
||
|
|
3. **Implement polling**:
|
||
|
|
- Poll every 1-2 seconds
|
||
|
|
- Track last message timestamp
|
||
|
|
- Only fetch new messages
|
||
|
|
|
||
|
|
4. **Document limitations**:
|
||
|
|
- Note the polling delay
|
||
|
|
- Explain why WebSocket doesn't work yet
|
||
|
|
- Provide upgrade path
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Status**: HTTP POST endpoint discovered and verified ✅
|
||
|
|
**Next**: Implement HTTP-based client to replace WebSocket
|
||
|
|
|