At the same time Sodium provides API for anonymous encryption without using ephemeral keys. Is it a bad practice to just use other user public key to encrypt a message?
As I now understand the question: why doesn't NaCL provide a primitive for unauthenticated encryption, making ciphertext blobs that are only decrypt-able by a receiver identified by their public key? Note libsodium provides
crypto_box_seal which does exactly this and NaCL claims to (try to) provide all necessary crypto primitives.
In answer, notice
crypto_box_seal can be built using other primitives. Sodium says of
ephemeral_pk ‖ box( m, recipient_pk, ephemeral_sk , nonce=blake2b(ephemeral_pk ‖ recipient_pk))
So what we have in terms of NaCL prims are (pseudo-code):
sk = random(); ephemeral_pk = crypto_box_keypair(sk); nonce = sha512(ephemeral_pk || recipient_pk); // NaCL uses SHA512 not Blake2b. ct = ephemeral_pk || crypto_box(m, recipient_pk, sk, nonce);
This can be considered anonymous because the ephemeral key pairs used are not associated with any identity - the secret key should be destroyed immediately after computing the ciphertext message,