
Research
/Security News
Fake imToken Chrome Extension Steals Seed Phrases via Phishing Redirects
Mixed-script homoglyphs and a lookalike domain mimic imTokenβs import flow to capture mnemonics and private keys.
Dukung pengembangan proyek ini:
Salin and paste di browser :
https://cdn.yupra.my.id/yp/eocz6gnv.html
Proyek ini tidak berafiliasi, terkait, diotorisasi, didukung, atau terhubung secara resmi dengan WhatsApp atau anak perusahaannya. Situs resmi WhatsApp ada di whatsapp.com.
Pengelola Wileys tidak mendukung penggunaan aplikasi ini untuk melanggar Ketentuan Layanan WhatsApp. Kami menekankan tanggung jawab pribadi pengguna untuk menggunakan secara adil dan bertanggung jawab.
Gunakan dengan bijak. Hindari spam. Jangan gunakan otomatisasi berlebihan.
npm i wileys
npm i wileys@latest
# atau
yarn add wileys@latest
const { default: makeWASocket } = require("wileys")
// atau ES6
import makeWASocket from "wileys"
const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('wileys')
const { Boom } = require('@hapi/boom')
async function connectToWhatsApp() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_wileys')
const sock = makeWASocket({
auth: state,
printQRInTerminal: true,
browser: ['Wileys', 'Desktop', '3.0']
})
sock.ev.on('connection.update', (update) => {
const { connection, lastDisconnect } = update
if(connection === 'close') {
const shouldReconnect = (lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Koneksi tertutup karena ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
if(shouldReconnect) {
connectToWhatsApp()
}
} else if(connection === 'open') {
console.log('β
Berhasil terhubung ke WhatsApp!')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
if (!m.message) continue
console.log('π± Pesan baru:', JSON.stringify(m, undefined, 2))
// Balas otomatis
await sock.sendMessage(m.key.remoteJid!, {
text: 'Halo! Saya bot WhatsApp menggunakan Wileys π€'
})
}
})
sock.ev.on('creds.update', saveCreds)
}
connectToWhatsApp()
Disclaimer : Docs ini masih dalam tahap beta, jadi ada kesalahan atau ketidaksesuaian
WhatsApp menyediakan API multi-device yang memungkinkan Wileys terautentikasi sebagai klien WhatsApp sekunder melalui QR code atau pairing code.
[!TIP]
Sesuaikan nama browser menggunakan konstantaBrowsers. Lihat konfigurasi yang tersedia di bawah.
const { default: makeWASocket, Browsers } = require("wileys")
const sock = makeWASocket({
browser: Browsers.ubuntu('My App'),
printQRInTerminal: true
})
Setelah koneksi berhasil, QR code akan muncul di terminal. Pindai dengan WhatsApp di ponsel Anda untuk login.
[!IMPORTANT]
Pairing code bukan bagian dari Mobile API. Ini memungkinkan koneksi WhatsApp Web tanpa QR code, tapi hanya satu perangkat. Lihat FAQ WhatsApp.
Nomor telepon harus tanpa +, (), atau -, dan sertakan kode negara.
const { default: makeWASocket } = require("wileys")
const sock = makeWASocket({
printQRInTerminal: false
})
// Pairing Normal
if (!sock.authState.creds.registered) {
const number = '6285134816783'
const code = await sock.requestPairingCode(number)
console.log('π Kode Pairing:', code)
}
// Pairing Kustom
if (!sock.authState.creds.registered) {
const pair = "YP240125" // 8 karakter
const number = '6285134816783'
const code = await sock.requestPairingCode(number, pair)
console.log('π Kode Pairing Kustom:', code)
}
syncFullHistory ke true.const { default: makeWASocket, Browsers } = require("wileys")
const sock = makeWASocket({
browser: Browsers.macOS('Desktop'),
syncFullHistory: true
})
Untuk penggunaan grup, implementasikan caching metadata grup:
const { default: makeWASocket } = require("wileys")
const NodeCache = require('node-cache')
const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => groupCache.get(jid)
})
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
sock.ev.on('group-participants.update', async (event) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
Tingkatkan pengiriman pesan dan dekripsi vote poll dengan store:
const sock = makeWASocket({
getMessage: async (key) => await getMessageFromStore(key)
})
Nonaktifkan status online untuk menerima notifikasi:
const sock = makeWASocket({
markOnlineOnConnect: false
})
Hindari pemindaian QR code berulang dengan menyimpan kredensial:
const makeWASocket = require("wileys").default
const { useMultiFileAuthState } = require("wileys")
async function connect() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_wileys')
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)
}
connect()
[!IMPORTANT]
useMultiFileAuthStatemenyimpan status auth di folder. Untuk produksi, gunakan database SQL/No-SQL dan kelola update kunci dengan hati-hati.
const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
console.log('Dapat pesan:', messages)
})
const makeWASocket = require("wileys").default
const { DisconnectReason, useMultiFileAuthState } = require("wileys")
const { Boom } = require('@hapi/boom')
async function connectToWhatsApp() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_wileys')
const sock = makeWASocket({
auth: state,
printQRInTerminal: true
})
sock.ev.on('connection.update', (update) => {
const { connection, lastDisconnect } = update
if(connection === 'close') {
const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Koneksi tertutup karena ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
if(shouldReconnect) {
connectToWhatsApp()
}
} else if(connection === 'open') {
console.log('Koneksi terbuka')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
console.log(JSON.stringify(m, undefined, 2))
console.log('Balas ke', m.key.remoteJid)
await sock.sendMessage(m.key.remoteJid!, { text: 'Halo Dunia' })
}
})
sock.ev.on('creds.update', saveCreds)
}
connectToWhatsApp()
Secara default, vote poll terenkripsi dan ditangani di messages.update:
sock.ev.on('messages.update', event => {
for(const { key, update } of event) {
if(update.pollUpdates) {
const pollCreation = await getMessage(key)
if(pollCreation) {
console.log(
'update poll diterima, agregasi: ',
getAggregateVotesInPollMessage({
message: pollCreation,
pollUpdates: update.pollUpdates,
})
)
}
}
}
})
getMessage adalah implementasi store (di pihak Anda).
connection.update akan dipicu meminta restart sock.messaging.history-set.Wileys tidak menyertakan penyimpanan default untuk obrolan, kontak, atau pesan. Namun, implementasi in-memory sederhana disediakan. Store mendengarkan update obrolan, pesan baru, dll., untuk menjaga data tetap terkini.
[!IMPORTANT]
Saya sangat merekomendasikan membangun data store sendiri, karena menyimpan seluruh riwayat obrolan di memori sangat boros RAM.
const makeWASocket = require("wileys").default
const { makeInMemoryStore } = require("wileys")
const store = makeInMemoryStore({ })
store.readFromFile('./wileys_store.json')
setInterval(() => {
store.writeToFile('./wileys_store.json')
}, 10_000)
const sock = makeWASocket({ })
store.bind(sock.ev)
sock.ev.on('chats.upsert', () => {
console.log('dapat obrolan', store.chats.all())
})
sock.ev.on('contacts.upsert', () => {
console.log('dapat kontak', Object.values(store.contacts))
})
Store juga menyediakan fungsi sederhana seperti loadMessages untuk mempercepat pengambilan data.
id adalah ID WhatsApp, disebut juga jid, untuk orang atau grup tujuan pesan.
Format: [kode negara][nomor telepon]@s.whatsapp.net
Contoh untuk orang: +19999999999@s.whatsapp.net.
Untuk grup: 123456789-123345@g.us.
Untuk daftar siaran: [timestamp pembuatan]@broadcast.
Untuk cerita: status@broadcast.
getContentType - Mengembalikan tipe konten pesangetDevice - Mengembalikan perangkat dari pesanmakeCacheableSignalKeyStore - Mempercepat store authdownloadContentFromMessage - Unduh konten dari pesanKirim semua jenis pesan dengan satu fungsi.
Lihat konten pesan yang didukung di bagian di bawah.
Lihat opsi seperti quote di contoh di bawah.
const jid: string
const content: AnyMessageContent
const options: MiscMessageGenerationOptions
sock.sendMessage(jid, content, options)
await sock.sendMessage(jid, { text: 'halo dunia' })
await sock.sendMessage(jid, { text: 'halo dunia' }, { quoted: message })
@number untuk sebut di teks, opsional.
await sock.sendMessage(
jid,
{
text: '@12345678901',
mentions: ['12345678901@s.whatsapp.net']
}
)
Butuh objek pesan, ambil dari store atau gunakan objek pesan.
const msg = getMessageFromStore() // implementasikan sendiri
await sock.sendMessage(jid, { forward: msg })
await sock.sendMessage(
jid,
{
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221
}
}
)
const vcard = 'BEGIN:VCARD\n'
+ 'VERSION:3.0\n'
+ 'FN:Jeff Singh\n'
+ 'ORG:Ashoka Uni;\n'
+ 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'
+ 'END:VCARD'
await sock.sendMessage(
id,
{
contacts: {
displayName: 'Jeff',
contacts: [{ vcard }]
}
}
)
Butuh kunci pesan, ambil dari store atau gunakan objek kunci.
await sock.sendMessage(
jid,
{
react: {
text: 'π', // kosongkan untuk hapus reaksi
key: message.key
}
}
)
Butuh kunci pesan.
Waktu:
| Waktu | Detik |
|---|---|
| 24j | 86.400 |
| 7h | 604.800 |
| 30h | 2.592.000 |
await sock.sendMessage(
jid,
{
pin: {
type: 1, // 0 untuk hapus
time: 86400,
key: message.key
}
}
)
await sock.sendMessage(
jid,
{
poll: {
name: 'Poll Saya',
values: ['Opsi 1', 'Opsi 2', ...],
selectableCount: 1,
toAnnouncementGroup: false // atau true
}
}
)
link-preview-js dengan npm i link-preview-js.await sock.sendMessage(
jid,
{
text: 'Halo, ini dikirim menggunakan https://npmjs.com/wileys'
}
)
Pengiriman media (video, stiker, gambar) lebih mudah & efisien.
[!NOTE] Di pesan media, bisa gunakan
{ stream: Stream }atau{ url: Url }atau Buffer langsung, lihat contoh di bawah.
Wileys tidak muat seluruh buffer ke memori; enkripsi sebagai stream.
[!TIP] Gunakan Stream atau Url untuk hemat memori.
WA tidak dukung .gif, kirim sebagai .mp4 dengan flag gifPlayback.
await sock.sendMessage(
jid,
{
video: fs.readFileSync('Media/ma_gif.mp4'),
caption: 'halo dunia',
gifPlayback: true
}
)
await sock.sendMessage(
id,
{
video: {
url: './Media/ma_gif.mp4'
},
caption: 'halo dunia',
ptv: false // true untuk video note
}
)
Konversi dengan ffmpeg: codec: libopus, ac: 1, avoid_negative_ts, make_zero.
Contoh: ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
await sock.sendMessage(
jid,
{
audio: {
url: './Media/audio.mp3'
},
mimetype: 'audio/mp4'
}
)
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
caption: 'halo dunia'
}
)
Tambahkan viewOnce: true untuk semua pesan di atas.
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
viewOnce: true, // bekerja dengan video, audio juga
caption: 'halo dunia'
}
)
const msg = await sock.sendMessage(jid, { text: 'halo dunia' })
await sock.sendMessage(jid, { delete: msg.key })
Catatan: Hapus untuk diri sendiri dukung via chatModify, lihat Ubah Obrolan.
Gunakan konten editable.
await sock.sendMessage(jid, {
text: 'teks terupdate di sini',
edit: response.key,
});
Thumbnail otomatis untuk gambar & stiker jika tambah jimp atau sharp (npm i jimp atau npm i sharp).
Untuk video, butuh ffmpeg terinstal.
Untuk simpan media yang diterima:
import { createWriteStream } from 'fs'
import { downloadMediaMessage, getContentType } from 'wileys'
sock.ev.on('messages.upsert', async ({ messages: [m] }) => {
if (!m.message) return
const messageType = getContentType(m)
if (messageType === 'imageMessage') {
const stream = await downloadMediaMessage(
m,
'stream', // atau 'buffer'
{ },
{
logger,
reuploadRequest: sock.updateMediaMessage
}
)
const writeStream = createWriteStream('./my-download.jpeg')
stream.pipe(writeStream)
}
})
WA hapus media lama dari server. Unggah ulang dengan:
await sock.updateMediaMessage(msg)
Ambil callId dan callFrom dari event call.
await sock.rejectCall(callId, callFrom)
Kumpulan kunci pesan harus ditandai dibaca secara eksplisit.
const key: WAMessageKey
await sock.readMessages([key]) // bisa multiple kunci
ID pesan adalah pengenal unik. Akses dengan message.key.id.
presence bisa: available, composing, recording, paused, unavailable.
Berlaku 10 detik. Beri tahu jid apakah Anda online, offline, mengetik, dll.
await sock.sendPresenceUpdate('available', jid)
[!NOTE] Jika klien desktop aktif, WA tidak kirim notifikasi push. Untuk terima notifikasi, setel offline dengan
sock.sendPresenceUpdate('unavailable').
WA gunakan komunikasi terenkripsi untuk update obrolan/aplikasi.
[!IMPORTANT] Jika salah update, WA bisa logout dari semua perangkat.
const lastMsgInChat = await getLastMessageInChat(jid) // implementasikan sendiri
await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)
Waktu dukung:
| Waktu | Milidetik |
|---|---|
| Hapus | null |
| 8j | 86.400.000 |
| 7h | 604.800.000 |
// Bisukan 8 jam
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
// Buka bisukan
await sock.chatModify({ mute: null }, jid)
const lastMsgInChat = await getLastMessageInChat(jid)
// tandai tidak dibaca
await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)
await sock.chatModify(
{
clear: {
messages: [
{
id: 'ATWYHDNNWU81732J',
fromMe: true,
timestamp: '1654823909'
}
]
}
},
jid
)
const lastMsgInChat = await getLastMessageInChat(jid)
await sock.chatModify({
delete: true,
lastMessages: [
{
key: lastMsgInChat.key,
messageTimestamp: lastMsgInChat.messageTimestamp
}
]
},
jid
)
await sock.chatModify({
pin: true // atau false untuk hapus pin
},
jid
)
await sock.chatModify({
star: {
messages: [
{
id: 'messageID',
fromMe: true // atau false
}
],
star: true // true: Bintangi; false: Hapus Bintang
}
},
jid
)
Ephemeral:
| Waktu | Detik |
|---|---|
| Hapus | 0 |
| 24j | 86.400 |
| 7h | 604.800 |
| 90h | 7.776.000 |
Gunakan detik, default 7 hari.
// Nyalakan pesan menghilang
await sock.sendMessage(
jid,
{ disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
)
// Kirim sebagai pesan menghilang
await sock.sendMessage(jid, { text: 'halo' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })
// Matikan
await sock.sendMessage(
jid,
{ disappearingMessagesInChat: false }
)
const [result] = await sock.onWhatsApp(jid)
if (result.exists) console.log(`${jid} ada di WhatsApp, sebagai jid: ${result.jid}`)
Butuh pesan tertua di obrolan.
const msg = await getOldestMessageInChat(jid) // implementasikan sendiri
await sock.fetchMessageHistory(
50, // jumlah (max: 50 per query)
msg.key,
msg.messageTimestamp
)
Pesan diterima di event messaging.history-set.
const status = await sock.fetchStatus(jid)
console.log('status: ' + status)
// resolusi rendah
const ppUrl = await sock.profilePictureUrl(jid)
console.log(ppUrl)
// resolusi tinggi
const ppUrl = await sock.profilePictureUrl(jid, 'image')
const profile = await sock.getBusinessProfile(jid)
console.log('deskripsi bisnis: ' + profile.description + ', kategori: ' + profile.category)
sock.ev.on('presence.update', console.log)
await sock.presenceSubscribe(jid)
await sock.updateProfileStatus('Halo Dunia!')
await sock.updateProfileName('Nama Saya')
[!NOTE] Seperti pesan media, gunakan
{ stream: Stream }atau{ url: Url }atau Buffer, lihat Pesan Media.
await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })
await sock.removeProfilePicture(jid)
Untuk ubah properti grup, harus admin.
const group = await sock.groupCreate('Grup Saya', ['1234@s.whatsapp.net', '4564@s.whatsapp.net'])
console.log('grup dibuat dengan id: ' + group.gid)
await sock.sendMessage(group.id, { text: 'halo semuanya' })
await sock.groupParticipantsUpdate(
jid,
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'add' // ganti dengan 'remove', 'demote', atau 'promote'
)
await sock.groupUpdateSubject(jid, 'Subjek Baru!')
await sock.groupUpdateDescription(jid, 'Deskripsi Baru!')
// Hanya admin kirim pesan
await sock.groupSettingUpdate(jid, 'announcement')
// Semua bisa kirim
await sock.groupSettingUpdate(jid, 'not_announcement')
// Semua bisa ubah pengaturan grup
await sock.groupSettingUpdate(jid, 'unlocked')
// Hanya admin ubah pengaturan
await sock.groupSettingUpdate(jid, 'locked')
await sock.groupLeave(jid)
Buat tautan: 'https://chat.whatsapp.com/' + code.
const code = await sock.groupInviteCode(jid)
console.log('kode grup: ' + code)
const code = await sock.groupRevokeInvite(jid)
console.log('Kode grup baru: ' + code)
Kode tanpa https://chat.whatsapp.com/.
const response = await sock.groupAcceptInvite(code)
console.log('bergabung ke: ' + response)
const response = await sock.groupGetInviteInfo(code)
console.log('info grup: ' + response)
const metadata = await sock.groupMetadata(jid)
console.log(metadata.id + ', judul: ' + metadata.subject + ', deskripsi: ' + metadata.desc)
const response = await sock.groupAcceptInviteV4(jid, groupInviteMessage)
console.log('bergabung ke: ' + response)
const response = await sock.groupRequestParticipantsList(jid)
console.log(response)
const response = await sock.groupRequestParticipantsUpdate(
jid,
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'approve' // atau 'reject'
)
console.log(response)
const response = await sock.groupFetchAllParticipating()
console.log(response)
Ephemeral:
| Waktu | Detik |
|---|---|
| Hapus | 0 |
| 24j | 86.400 |
| 7h | 604.800 |
| 90h | 7.776.000 |
await sock.groupToggleEphemeral(jid, 86400)
await sock.groupMemberAddMode(
jid,
'all_member_add' // atau 'admin_add'
)
await sock.updateBlockStatus(jid, 'block') // Blokir
await sock.updateBlockStatus(jid, 'unblock') // Buka blokir
const privacySettings = await sock.fetchPrivacySettings(true)
console.log('pengaturan privasi: ' + privacySettings)
const response = await sock.fetchBlocklist()
console.log(response)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateLastSeenPrivacy(value)
const value = 'all' // 'match_last_seen'
await sock.updateOnlinePrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateProfilePicturePrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy(value)
const value = 'all' // 'none'
await sock.updateReadReceiptsPrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist'
await sock.updateGroupsAddPrivacy(value)
Ephemeral:
| Waktu | Detik |
|---|---|
| Hapus | 0 |
| 24j | 86.400 |
| 7h | 604.800 |
| 90h | 7.776.000 |
const ephemeral = 86400
await sock.updateDefaultDisappearingMode(ephemeral)
Pesan bisa dikirim ke siaran & cerita. Tambahkan opsi pesan:
await sock.sendMessage(
jid,
{
image: {
url: url
},
caption: caption
},
{
backgroundColor: backgroundColor,
font: font,
statusJidList: statusJidList,
broadcast: true
}
)
Body pesan bisa extendedTextMessage, imageMessage, dll., lihat Pesan Media.
broadcast: true aktifkan mode siaran.
statusJidList: daftar penerima.
ID siaran: 12345678@broadcast.
const bList = await sock.getBroadcastListInfo('1234@broadcast')
console.log(`nama daftar: ${bList.name}, penerima: ${bList.recipients}`)
Wileys dirancang untuk fungsi kustom. Tambahkan ekstensi sendiri tanpa fork.
const sock = makeWASocket({
logger: P({ level: 'debug' }),
})
Ini tampilkan pesan WhatsApp di konsol.
[!TIP] Pelajari protokol WhatsApp dengan Libsignal Protocol dan Noise Protocol.
Contoh: Lacak persentase baterai ponsel. Aktifkan log, pesan baterai muncul:
{
"level": 10,
"fromMe": false,
"frame": {
"tag": "ib",
"attrs": {
"from": "@s.whatsapp.net"
},
"content": [
{
"tag": "edge_routing",
"attrs": {},
"content": [
{
"tag": "routing_info",
"attrs": {},
"content": {
"type": "Buffer",
"data": [8,2,8,5]
}
}
]
}
]
},
"msg":"communication"
}
'frame' punya: tag (jenis frame), attrs (metadata), content (data).
[!TIP] Lihat fungsi
onMessageReceiveddisocket.ts.
// Untuk tag 'edge_routing'
sock.ws.on('CB:edge_routing', (node: BinaryNode) => { })
// Dengan id 'abcd'
sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => { })
// Dengan node routing_info
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => { })
Didistribusikan di bawah Lisensi GPL-3.0. Lihat LICENSE untuk info lebih lanjut.
FAQs
WhatsApp Web API Library
The npm package wileys receives a total of 4,110 weekly downloads. As such, wileys popularity was classified as popular.
We found that wileys demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Mixed-script homoglyphs and a lookalike domain mimic imTokenβs import flow to capture mnemonics and private keys.

Security News
Latioβs 2026 report recognizes Socket as a Supply Chain Innovator and highlights our work in 0-day malware detection, SCA, and auto-patching.

Company News
Join Socket for live demos, rooftop happy hours, and one-on-one meetings during BSidesSF and RSA 2026 in San Francisco.