Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e6ac912
typo (#28965)
chrisnojima Mar 3, 2026
bc321cb
add tabIndex to stop going from hidden to showing through widget caus…
chrisnojima Mar 10, 2026
0e7b9db
fix widget more (#29005)
chrisnojima Mar 10, 2026
efe2d20
Fix outstanding appendGUILogs session in node process (#29001) (#29006)
chrisnojima Mar 10, 2026
cc71edf
Bump version to 6.6.1 (#29007)
chrisnojima Mar 10, 2026
0c522b4
Fix missing copy/move menu item in files tab (#29009)
chrisnojima Mar 11, 2026
2602d17
fix bailing early updating reactions (#29011)
chrisnojima Mar 12, 2026
79b9dfc
Copy edit exploding error messages, fix snippet for exploders (#29020)
zoom-ua Mar 16, 2026
dcf9c54
pin avdl-compiler (#29024)
zoom-ua Mar 17, 2026
de09dce
additional logging around startup (#29022)
zoom-ua Mar 17, 2026
8b8c50f
Fix git clone failing when default branch is not master (#29004)
chrisnojima Mar 18, 2026
898b99b
changelog (#29027)
chrisnojima Mar 18, 2026
3898f26
upgrade xpath (#29028)
zoom-ua Mar 18, 2026
e77ea8e
gitignore watchman cookie (#29031)
zoom-ua Mar 18, 2026
1f196bf
set 100ms timeout on Linking.getInitialURL, more logs (#29032)
zoom-ua Mar 18, 2026
510a5f7
notifyJSReady logging (#29036)
zoom-ua Mar 19, 2026
199f6a6
remvoe timout on Linking.getInitialURL (#29037)
zoom-ua Mar 19, 2026
68215fb
main thread watchdog, flush buffered writer (#29042)
zoom-ua Mar 20, 2026
02c4e2c
signal handler (#29045)
zoom-ua Mar 23, 2026
d453a98
fix crash: use throwing FileHandle APIs so do/catch guards against wr…
zoom-ua Mar 23, 2026
e9488ec
add advertisements to tuxbot (#29047)
zoom-ua Mar 23, 2026
b5a7bc2
edge to edge fix (#29048)
chrisnojima Mar 23, 2026
f003df2
update 6.6.2 (#29051)
chrisnojima Mar 23, 2026
fb189fd
more startup debugging (#29050)
zoom-ua Mar 23, 2026
4018d61
Modernize swift code (#29054)
zoom-ua Mar 24, 2026
b4fd646
startup debug logging (#29055)
zoom-ua Mar 24, 2026
cde43cd
increase watchdog frequency (#29067)
zoom-ua Mar 25, 2026
ed7e7cc
fix fmt pod for new xcode (#29068)
zoom-ua Mar 25, 2026
08025f3
add patch so fmt change isn't lost (#29075)
chrisnojima Mar 26, 2026
664ebbf
CI: clean yarn cache (#29079)
zoom-ua Mar 26, 2026
d8f16d1
bump golang.org/x/image 0.38.0 (#29077)
zoom-ua Mar 26, 2026
005a154
Startup timing (#29078)
zoom-ua Mar 26, 2026
cadb632
restart loopback server on dial fail (#29086)
zoom-ua Mar 27, 2026
8fdf422
startup fixes (#29091)
zoom-ua Mar 28, 2026
9276ad0
bridge lifecycle logging (#29097)
zoom-ua Mar 31, 2026
00b32db
more debug logging (#29100)
zoom-ua Apr 1, 2026
8d5bf7d
fix sourceURL (#29101)
zoom-ua Apr 1, 2026
9602c11
Move startup log writes off the main thread (#29103)
zoom-ua Apr 1, 2026
8a5af1d
store init err for logging (#29104)
zoom-ua Apr 1, 2026
22f45ce
fix logfile permissions/cleanup (#29105)
zoom-ua Apr 1, 2026
96da9ab
Revert unneeded startup debugging (#29107)
zoom-ua Apr 2, 2026
70cbb6e
rm .cursorrules (#29110)
zoom-ua Apr 2, 2026
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
132 changes: 0 additions & 132 deletions .cursorrules

This file was deleted.

1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ helpers.rootLinuxNode(env, {
sh "go install mvdan.cc/gofumpt"
}
dir ('protocol') {
sh "yarn cache clean avdl-compiler"
sh "yarn --frozen-lockfile"
sh "make clean"
sh "make"
Expand Down
84 changes: 68 additions & 16 deletions go/bind/keybase.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,29 @@ var (
connMutex sync.Mutex // Protects conn operations
)

// log writes to kbCtx.Log if available, otherwise falls back to fmt.Printf
func describeConn(c net.Conn) string {
if c == nil {
return "<nil>"
}
return fmt.Sprintf("%T@%p", c, c)
}

func appStateForLog() string {
if kbCtx == nil || kbCtx.MobileAppState == nil {
return "<unknown>"
}
return fmt.Sprintf("%v", kbCtx.MobileAppState.State())
}

// log writes to kbCtx.Log if available, otherwise falls back to stderr.
// Stderr is captured in crash logs and the Xcode console, making early Init
// messages (before kbCtx.Log is set up by Configure) visible in diagnostics.
func log(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if kbCtx != nil && kbCtx.Log != nil {
kbCtx.Log.Info(msg)
} else {
fmt.Printf("%s\n", msg)
fmt.Fprintf(os.Stderr, "keybase: %s\n", msg)
}
}

Expand Down Expand Up @@ -469,9 +485,13 @@ func WriteArr(b []byte) (err error) {

n, err := currentConn.Write(bytes)
if err != nil {
log("Go: WriteArr error conn=%s len=%d appState=%s err=%v",
describeConn(currentConn), len(bytes), appStateForLog(), err)
return fmt.Errorf("Write error: %s", err)
}
if n != len(bytes) {
log("Go: WriteArr short write conn=%s wrote=%d expected=%d appState=%s",
describeConn(currentConn), n, len(bytes), appStateForLog())
return errors.New("Did not write all the data")
}
return nil
Expand Down Expand Up @@ -525,18 +545,37 @@ func ReadArr() (data []byte, err error) {
// Must be called with connMutex held.
func ensureConnection() error {
if !isInited() {
log("ensureConnection: keybase not initialized")
return errors.New("keybase not initialized")
}
if kbCtx == nil || kbCtx.LoopbackListener == nil {
log("ensureConnection: loopback listener not initialized (kbCtx nil: %v)", kbCtx == nil)
return errors.New("loopback listener not initialized")
}

var err error
conn, err = kbCtx.LoopbackListener.Dial()
if err != nil {
return fmt.Errorf("Failed to dial loopback listener: %s", err)
// The listener was closed (isClosed=true, returns syscall.EINVAL). Recreate it and
// start a new ListenLoop goroutine, then retry the dial once.
log("ensureConnection: Dial failed (%v), restarting loopback server", err)
l, rerr := kbCtx.MakeLoopbackServer()
if rerr != nil {
log("ensureConnection: MakeLoopbackServer failed: %v", rerr)
return fmt.Errorf("failed to restart loopback server: %s", rerr)
}
go func() { _ = kbSvc.ListenLoop(l) }()
conn, err = kbCtx.LoopbackListener.Dial()
if err != nil {
log("ensureConnection: Dial failed after restart: %v", err)
return fmt.Errorf("failed to dial after loopback restart: %s", err)
}
log("ensureConnection: loopback server restarted successfully conn=%s appState=%s",
describeConn(conn), appStateForLog())
return nil
}
log("Go: Established loopback connection")
log("Go: Established loopback connection conn=%s appState=%s",
describeConn(conn), appStateForLog())
return nil
}

Expand All @@ -545,24 +584,28 @@ func Reset() error {
connMutex.Lock()
defer connMutex.Unlock()

log("Go: Reset start conn=%s appState=%s", describeConn(conn), appStateForLog())
if conn != nil {
conn.Close()
conn = nil
}
if kbCtx == nil || kbCtx.LoopbackListener == nil {
log("Go: Reset complete without listener appState=%s", appStateForLog())
return nil
}

// Connection will be re-established lazily on next read/write
log("Go: Connection reset, will reconnect on next operation")
log("Go: Connection reset, will reconnect on next operation appState=%s", appStateForLog())
return nil
}

// NotifyJSReady signals that the JavaScript side is ready to send/receive RPCs.
// This unblocks the ReadArr loop and allows bidirectional communication.
// jsReadyCh is closed once and stays closed — repeated calls from engine resets are no-ops.
func NotifyJSReady() {
jsReadyOnce.Do(func() {
log("Go: JS signaled ready, unblocking RPC communication")
log("Go: JS signaled ready, unblocking RPC communication appState=%s conn=%s",
appStateForLog(), describeConn(conn))
close(jsReadyCh)
})
}
Expand Down Expand Up @@ -590,6 +633,12 @@ func IsAppStateForeground() bool {
return kbCtx.MobileAppState.State() == keybase1.MobileAppState_FOREGROUND
}

// FlushLogs synchronously flushes any buffered log data to disk. Call this
// before background suspension and after foreground resume to prevent log loss.
func FlushLogs() {
logger.FlushLogFile()
}

func SetAppStateForeground() {
if !isInited() {
return
Expand Down Expand Up @@ -639,36 +688,39 @@ func waitForInit(maxDur time.Duration) error {
}
}

func BackgroundSync() {
// BackgroundSync runs a short background sync pulse. Returns a non-empty status
// string on early exit or error so Swift can log it via NSLog.
func BackgroundSync() string {
// On Android there is a race where this function can be called before Init when starting up in the
// background. Let's wait a little bit here for Init to get run, and bail out if it never does.
if err := waitForInit(5 * time.Second); err != nil {
return
return fmt.Sprintf("waitForInit timeout: %v", err)
}
defer kbCtx.Trace("BackgroundSync", nil)()

// Skip the sync if we aren't in the background
if state := kbCtx.MobileAppState.State(); state != keybase1.MobileAppState_BACKGROUND {
kbCtx.Log.Debug("BackgroundSync: skipping, app not in background state: %v", state)
return
msg := fmt.Sprintf("skipping, app not in background state: %v", state)
kbCtx.Log.Debug("BackgroundSync: %s", msg)
return msg
}

nextState := keybase1.MobileAppState_BACKGROUNDACTIVE
kbCtx.MobileAppState.Update(nextState)
doneCh := make(chan struct{})
resultCh := make(chan string, 1)
go func() {
defer func() { close(doneCh) }()
select {
case state := <-kbCtx.MobileAppState.NextUpdate(&nextState):
// if literally anything happens, let's get out of here
kbCtx.Log.Debug("BackgroundSync: bailing out early, appstate change: %v", state)
return
msg := fmt.Sprintf("bailing out early, appstate change: %v", state)
kbCtx.Log.Debug("BackgroundSync: %s", msg)
resultCh <- msg
case <-time.After(10 * time.Second):
kbCtx.MobileAppState.Update(keybase1.MobileAppState_BACKGROUND)
return
resultCh <- "completed 10s window"
}
}()
<-doneCh
return <-resultCh
}

// pushPendingMessageFailure sends at most one notification that a message
Expand Down
5 changes: 3 additions & 2 deletions go/chat/localizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,9 @@ func (s *localizerPipeline) localizeConversation(ctx context.Context, uid gregor
var maxValidID chat1.MessageID
s.Debug(ctx, "localizing %d max msgs", len(maxMsgs))
for _, mm := range maxMsgs {
if mm.IsValid() &&
utils.IsSnippetChatMessageType(mm.GetMessageType()) &&
isValidSnippet := mm.IsValid() && utils.IsSnippetChatMessageType(mm.GetMessageType())
isEphemeralErr := mm.IsError() && mm.Error().IsEphemeral
if (isValidSnippet || isEphemeralErr) &&
(conversationLocal.Info.SnippetMsg == nil ||
conversationLocal.Info.SnippetMsg.GetMessageID() < mm.GetMessageID()) {
conversationLocal.Info.SnippetMsg = new(chat1.MessageUnboxed)
Expand Down
19 changes: 14 additions & 5 deletions go/chat/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1116,10 +1116,13 @@ func formatDuration(dur time.Duration) string {

func getMsgSnippetDecoration(msg chat1.MessageUnboxed) chat1.SnippetDecoration {
var msgBody chat1.MessageBody
if msg.IsValid() {
switch {
case msg.IsValid():
msgBody = msg.Valid().MessageBody
} else {
case msg.IsOutbox():
msgBody = msg.Outbox().Msg.MessageBody
default:
return chat1.SnippetDecoration_NONE
}
switch msg.GetMessageType() {
case chat1.MessageType_ATTACHMENT:
Expand Down Expand Up @@ -1220,14 +1223,20 @@ func GetMsgSnippetBody(ctx context.Context, g *globals.Context, uid gregor1.UID,
func GetMsgSnippet(ctx context.Context, g *globals.Context, uid gregor1.UID, msg chat1.MessageUnboxed,
conv chat1.ConversationLocal, currentUsername string,
) (decoration chat1.SnippetDecoration, snippet string, snippetDecorated string) {
if !msg.IsValid() && !msg.IsOutbox() {
return chat1.SnippetDecoration_NONE, "", ""
}
defer func() {
if len(snippetDecorated) == 0 {
snippetDecorated = snippet
}
}()
if !msg.IsValid() && !msg.IsOutbox() {
if msg.IsError() && msg.Error().IsEphemeral {
if msg.Error().IsEphemeralExpired(time.Now()) {
return chat1.SnippetDecoration_EXPLODED_MESSAGE, "Message exploded.", ""
}
return chat1.SnippetDecoration_EXPLODING_MESSAGE, msg.Error().ErrMsg, ""
}
return chat1.SnippetDecoration_NONE, "", ""
}

var senderUsername string
if msg.IsValid() {
Expand Down
Loading